未验证 提交 aabb2ecd 编写于 作者: A Asher 提交者: GitHub

Update node to 10.15.1 (#472)

* Update Node to 10.15.1

* Remove string replace that was used for oclif

* Update nbin

* Package node-pty and spdlog with nbin

* Label stderr/stdout from shared process

* Remove fork override

* Prevent "already disposed" errors when trying to kill disposed proxies

* Include spdlog dependencies

* Shim /node_modules

* Add node_modules to Docker ignore

It keeps using my already-built .node files which results in a
mismatching GLIBC version error.

* Update nbin
上级 dfabc070
......@@ -7,3 +7,4 @@ doc/
.travis.yml
LICENSE
README.md
node_modules
language: node_js
node_js:
- 8.15.0
- 10.15.1
env:
- VSCODE_VERSION="1.33.1" MAJOR_VERSION="1" VERSION="$MAJOR_VERSION.$TRAVIS_BUILD_NUMBER-vsc$VSCODE_VERSION"
matrix:
......
FROM node:8.15.0
FROM node:10.15.1
# Install VS Code's deps. These are the only two it seems we need.
RUN apt-get update && apt-get install -y \
......
......@@ -48,19 +48,11 @@ const buildServerBinaryCopy = register("build:server:binary:copy", async (runner
const bootstrapForkPath = path.join(pkgsPath, "vscode", "out", "bootstrap-fork.js");
const webOutputPath = path.join(pkgsPath, "web", "out");
const browserAppOutputPath = path.join(pkgsPath, "app", "browser", "out");
const nodePtyModule = path.join(pkgsPath, "protocol", "node_modules", "node-pty-prebuilt", "build", "Release", "pty.node");
const spdlogModule = path.join(pkgsPath, "protocol", "node_modules", "spdlog", "build", "Release", "spdlog.node");
let ripgrepPath = path.join(pkgsPath, "..", "lib", "vscode", "node_modules", "vscode-ripgrep", "bin", "rg");
if (isWin) {
ripgrepPath += ".exe";
}
if (!fs.existsSync(nodePtyModule)) {
throw new Error("Could not find pty.node. Ensure all packages have been installed");
}
if (!fs.existsSync(spdlogModule)) {
throw new Error("Could not find spdlog.node. Ensure all packages have been installed");
}
if (!fs.existsSync(webOutputPath)) {
throw new Error("Web bundle must be built");
}
......@@ -91,8 +83,6 @@ const buildServerBinaryCopy = register("build:server:binary:copy", async (runner
cpDir(webOutputPath, "auth", webOutputPath);
cpDir(browserAppOutputPath, "unauth", browserAppOutputPath);
fse.mkdirpSync(path.join(cliBuildPath, "dependencies"));
fse.copySync(nodePtyModule, path.join(cliBuildPath, "dependencies", "pty.node"));
fse.copySync(spdlogModule, path.join(cliBuildPath, "dependencies", "spdlog.node"));
fse.copySync(ripgrepPath, path.join(cliBuildPath, "dependencies", "rg"));
});
......@@ -204,9 +194,9 @@ register("package", async (runner, releaseTag) => {
});
runner.cwd = releasePath;
await os.platform() === "linux"
await (os.platform() === "linux"
? runner.execute("tar", ["-cvzf", `${archiveName}.tar.gz`, `${archiveName}`])
: runner.execute("zip", ["-r", `${archiveName}.zip`, `${archiveName}`]);
: runner.execute("zip", ["-r", `${archiveName}.zip`, `${archiveName}`]));
});
run();
......@@ -56,8 +56,13 @@
"webpack-hot-middleware": "^2.24.3",
"write-file-webpack-plugin": "^4.5.0"
},
"resolutions": {
"bindings": "1.3.0"
},
"dependencies": {
"node-loader": "^0.6.0",
"node-pty": "0.8.1",
"spdlog": "0.8.1",
"webpack-merge": "^4.2.1"
}
}
......@@ -33,8 +33,7 @@
"@coder/(.*)/test": "<rootDir>/$1/test",
"@coder/(.*)": "<rootDir>/$1/src",
"vs/(.*)": "<rootDir>/../lib/vscode/src/vs/$1",
"vszip": "<rootDir>/../lib/vscode/src/vs/base/node/zip.ts",
"^node-pty": "node-pty-prebuilt"
"vszip": "<rootDir>/../lib/vscode/src/vs/base/node/zip.ts"
},
"transform": {
"^.+\\.tsx?$": "ts-jest"
......
......@@ -4,8 +4,6 @@
"dependencies": {
"express": "^4.16.4",
"google-protobuf": "^3.6.1",
"node-pty-prebuilt": "^0.7.6",
"spdlog": "^0.7.2",
"trash": "^4.3.0",
"ws": "^6.1.2"
},
......
......@@ -174,9 +174,10 @@ export class Client {
* Make a remote call for a proxy's method using proto.
*/
private remoteCall(proxyId: number | Module, method: string, args: any[]): Promise<any> {
if (this.disconnected && typeof proxyId === "number") {
// Can assume killing or closing works because a disconnected proxy
// is disposed on the server's side.
if (typeof proxyId === "number" && (this.disconnected || !this.proxies.has(proxyId))) {
// Can assume killing or closing works because a disconnected proxy is
// disposed on the server's side, and a non-existent proxy has already
// been disposed.
switch (method) {
case "close":
case "kill":
......
此差异已折叠。
......@@ -9,7 +9,7 @@
"build:binary": "ts-node scripts/nbin.ts"
},
"dependencies": {
"@coder/nbin": "^1.0.7",
"@coder/nbin": "^1.1.1",
"commander": "^2.19.0",
"express": "^4.16.4",
"express-static-gzip": "^1.1.3",
......
......@@ -10,6 +10,16 @@ const bin = new Binary({
});
bin.writeFiles(path.join(rootDir, "build", "**"));
bin.writeFiles(path.join(rootDir, "out", "**"));
[
// Native modules. These are marked as externals in the webpack config.
"spdlog",
"node-pty",
// These are spdlog's dependencies.
"mkdirp", "bindings",
].forEach((name) => {
bin.writeModule(path.join(__dirname, "../../../node_modules", name));
});
bin.build().then((binaryData) => {
const outputPath = path.join(__dirname, "..", `cli-${target}`);
fs.writeFileSync(outputPath, binaryData);
......
......@@ -8,9 +8,8 @@ import * as os from "os";
import * as path from "path";
import * as WebSocket from "ws";
import { buildDir, cacheHome, dataHome, isCli, serveStatic } from "./constants";
import { setup as setupNativeModules } from "./modules";
import { createApp } from "./server";
import { forkModule, requireFork, requireModule } from "./vscode/bootstrapFork";
import { forkModule, requireModule } from "./vscode/bootstrapFork";
import { SharedProcess, SharedProcessState } from "./vscode/sharedProcess";
import opn = require("opn");
......@@ -31,7 +30,6 @@ commander.version(process.env.VERSION || "development")
.option("-H, --allow-http", "Allow http connections.", false)
.option("-P, --password <value>", "Specify a password for authentication.")
.option("--bootstrap-fork <name>", "Used for development. Never set.")
.option("--fork <name>", "Used for development. Never set.")
.option("--extra-args <args>", "Used for development. Never set.")
.arguments("Specify working directory.")
.parse(process.argv);
......@@ -39,6 +37,7 @@ commander.version(process.env.VERSION || "development")
Error.stackTraceLimit = Infinity;
if (isCli) {
require("nbin").shimNativeFs(buildDir);
require("nbin").shimNativeFs("/node_modules");
}
// Makes strings or numbers bold in stdout
const bold = (text: string | number): string | number => {
......@@ -63,7 +62,6 @@ const bold = (text: string | number): string | number => {
readonly certKey?: string;
readonly bootstrapFork?: string;
readonly fork?: string;
readonly extraArgs?: string;
};
......@@ -75,6 +73,7 @@ const bold = (text: string | number): string | number => {
const dataDir = path.resolve(options.userDataDir || options.dataDir || path.join(dataHome, "code-server"));
const extensionsDir = options.extensionsDir ? path.resolve(options.extensionsDir) : path.resolve(dataDir, "extensions");
const workingDir = path.resolve(args[0] || process.cwd());
const dependenciesDir = path.join(os.tmpdir(), "code-server/dependencies");
if (!fs.existsSync(dataDir)) {
const oldDataDir = path.resolve(path.join(os.homedir(), ".code-server"));
......@@ -89,9 +88,22 @@ const bold = (text: string | number): string | number => {
fse.mkdirp(dataDir),
fse.mkdirp(extensionsDir),
fse.mkdirp(workingDir),
fse.mkdirp(dependenciesDir),
]);
setupNativeModules(dataDir);
const unpackExecutable = (binaryName: string): void => {
const memFile = path.join(isCli ? buildDir! : path.join(__dirname, ".."), "build/dependencies", binaryName);
const diskFile = path.join(dependenciesDir, binaryName);
if (!fse.existsSync(diskFile)) {
fse.writeFileSync(diskFile, fse.readFileSync(memFile));
}
fse.chmodSync(diskFile, "755");
};
unpackExecutable("rg");
// tslint:disable-next-line no-any
(<any>global).RIPGREP_LOCATION = path.join(dependenciesDir, "rg");
const builtInExtensionsDir = path.resolve(buildDir || path.join(__dirname, ".."), "build/extensions");
if (options.bootstrapFork) {
const modulePath = options.bootstrapFork;
......@@ -106,13 +118,7 @@ const bold = (text: string | number): string | number => {
process.argv[i + 2] = arg;
});
return requireModule(modulePath, dataDir, builtInExtensionsDir);
}
if (options.fork) {
const modulePath = options.fork;
return requireFork(modulePath, JSON.parse(options.extraArgs!), builtInExtensionsDir);
return requireModule(modulePath, builtInExtensionsDir);
}
const logDir = path.join(cacheHome, "code-server/logs", new Date().toISOString().replace(/[-:.TZ]/g, ""));
......@@ -224,14 +230,7 @@ const bold = (text: string | number): string | number => {
return forkModule(options.env.AMD_ENTRYPOINT, args, options, dataDir);
}
if (isCli) {
return spawn(process.execPath, [path.join(buildDir, "out", "cli.js"), "--fork", modulePath, "--extra-args", JSON.stringify(args), "--data-dir", dataDir], {
...options,
stdio: [null, null, null, "ipc"],
});
} else {
return fork(modulePath, args, options);
}
return fork(modulePath, args, options);
},
},
password,
......
import * as fs from "fs";
import * as path from "path";
import * as os from "os";
import { isCli, buildDir } from "./constants";
/**
* Handling of native modules within the CLI
*/
export const setup = (dataDirectory: string): void => {
path.resolve(dataDirectory, "dependencies").split(path.sep).reduce((parentDir, childDir) => {
const currentDir = path.join(parentDir, childDir);
try {
fs.mkdirSync(currentDir);
} catch (ex) {
if (ex.code !== "EEXIST" && ex.code !== "EISDIR" && ex.code !== "ENOENT") {
throw ex;
}
}
return currentDir;
}, os.platform() === "win32" ? undefined! : path.sep); // Might need path.sep here for linux. Having it for windows causes an error because \C:\Users ...
const unpackModule = (moduleName: string, markExecutable: boolean = false): void => {
const memFile = path.join(isCli ? buildDir! : path.join(__dirname, ".."), "build/dependencies", moduleName);
const diskFile = path.join(dataDirectory, "dependencies", moduleName);
if (!fs.existsSync(diskFile)) {
fs.writeFileSync(diskFile, fs.readFileSync(memFile));
}
if (markExecutable) {
fs.chmodSync(diskFile, "755");
}
};
/**
* We need to unpack node-pty and patch its `loadNative` function to require our unpacked pty.node
* If pty.node isn't unpacked a SIGSEGV is thrown and the application exits. The exact reasoning
* for this is unknown ATM, but this patch works around it.
*/
unpackModule("pty.node");
unpackModule("spdlog.node");
unpackModule("rg", true);
// const nodePtyUtils = require("../../protocol/node_modules/node-pty-prebuilt/lib/utils") as typeof import("../../protocol/node_modules/node-pty-prebuilt/src/utils");
// tslint:disable-next-line:no-any
// nodePtyUtils.loadNative = (modName: string): any => {
// return (typeof __non_webpack_require__ !== "undefined" ? __non_webpack_require__ : require)(path.join(dataDirectory, "dependencies", modName + ".node"));
// };
(<any>global).RIPGREP_LOCATION = path.join(dataDirectory, "dependencies", "rg");
(<any>global).NODEPTY_LOCATION = path.join(dataDirectory, "dependencies", "pty.node");
// tslint:disable-next-line:no-any
(<any>global).SPDLOG_LOCATION = path.join(dataDirectory, "dependencies", "spdlog.node");
// tslint:disable-next-line:no-unused-expression
require("../../protocol/node_modules/node-pty-prebuilt/lib/index") as typeof import("../../protocol/node_modules/node-pty-prebuilt/src/index");
};
......@@ -42,54 +42,13 @@ const requireFilesystemModule = (id: string, builtInExtensionsDir: string): any
return customMod.require(id);
};
/**
* Called from forking a module
*/
export const requireFork = (modulePath: string, args: string[], builtInExtensionsDir: string): void => {
const Module = require("module") as typeof import("module");
const oldRequire = Module.prototype.require;
// tslint:disable-next-line:no-any
const oldLoad = (Module as any)._findPath;
// @ts-ignore
(Module as any)._findPath = function (request, parent, isMain): any {
const lookupPaths = oldLoad.call(this, request, parent, isMain);
return lookupPaths;
};
// tslint:disable-next-line:no-any
Module.prototype.require = function (id: string): any {
if (id === "typescript") {
return require("typescript");
}
// tslint:disable-next-line:no-any
return oldRequire.call(this, id as any);
};
if (!process.send) {
throw new Error("No IPC messaging initialized");
}
process.argv = ["", "", ...args];
requireFilesystemModule(modulePath, builtInExtensionsDir);
if (ipcMsgBuffer && ipcMsgListener) {
process.removeListener("message", ipcMsgListener);
// tslint:disable-next-line:no-any
ipcMsgBuffer.forEach((i) => process.emit("message" as any, i as any));
ipcMsgBuffer = undefined;
ipcMsgListener = undefined;
}
};
export const requireModule = (modulePath: string, dataDir: string, builtInExtensionsDir: string): void => {
export const requireModule = (modulePath: string, builtInExtensionsDir: string): void => {
process.env.AMD_ENTRYPOINT = modulePath;
const xml = require("xhr2");
xml.XMLHttpRequest.prototype._restrictedHeaders["user-agent"] = false;
// tslint:disable-next-line no-any this makes installing extensions work.
(global as any).XMLHttpRequest = xml.XMLHttpRequest;
const mod = require("module") as typeof import("module");
const promiseFinally = require("promise.prototype.finally") as { shim: () => void };
promiseFinally.shim();
/**
......@@ -102,16 +61,7 @@ export const requireModule = (modulePath: string, dataDir: string, builtInExtens
};
if (isCli) {
/**
* Needed for properly forking external modules within the CLI
*/
// tslint:disable-next-line:no-any
(<any>cp).fork = (modulePath: string, args: ReadonlyArray<string> = [], options?: cp.ForkOptions): cp.ChildProcess => {
return cp.spawn(process.execPath, [path.join(buildDir, "out", "cli.js"), "--fork", modulePath, "--extra-args", JSON.stringify(args), "--data-dir", dataDir], {
...options,
stdio: [null, null, null, "ipc"],
});
};
process.env.NBIN_BYPASS = "true";
}
const baseDir = path.join(buildDir, "build");
......
......@@ -8,7 +8,7 @@ import { StdioIpcHandler } from "../ipc";
import { ParsedArgs } from "vs/platform/environment/common/environment";
import { Emitter } from "@coder/events/src";
import { retry } from "@coder/ide/src/retry";
import { logger, Level } from "@coder/logger";
import { logger, field, Level } from "@coder/logger";
export enum SharedProcessState {
Stopped,
......@@ -127,13 +127,13 @@ export class SharedProcess {
activeProcess.on("exit", doReject);
activeProcess.stdout.on("data", (data) => {
logger.trace(data.toString());
logger.trace("stdout", field("data", data.toString()));
});
activeProcess.stderr.on("data", (data) => {
// Warn instead of error to prevent panic. It's unlikely stderr here is
// about anything critical to the functioning of the editor.
logger.warn(data.toString());
logger.warn("stderr", field("data", data.toString()));
});
this.ipcHandler = new StdioIpcHandler(activeProcess);
......
......@@ -13,7 +13,6 @@ module.exports = merge(
path: path.join(__dirname, "out"),
libraryTarget: "commonjs",
},
mode: "production",
node: {
console: false,
global: false,
......@@ -23,14 +22,8 @@ module.exports = merge(
__dirname: false,
setImmediate: false
},
resolve: {
alias: {
"node-pty": "node-pty-prebuilt",
},
},
externals: {
"nbin": "commonjs nbin",
"fsevents": "fsevents",
},
entry: "./packages/server/src/cli.ts",
plugins: [
......
......@@ -7,10 +7,10 @@
resolved "https://registry.yarnpkg.com/@coder/logger/-/logger-1.0.3.tgz#e0e1ae5496fde5a3c6ef3d748fdfb26a55add8b8"
integrity sha512-1o5qDZX2VZUNnzgz5KfAdMnaqaX6FNeTs0dUdg73MRHfQW94tFTIryFC1xTTCuzxGDjVHOHkaUAI4uHA2bheOA==
"@coder/nbin@^1.0.7":
version "1.0.7"
resolved "https://registry.yarnpkg.com/@coder/nbin/-/nbin-1.0.7.tgz#fc6adeb8366bf9d7dc7c301ce0be9e741eccf14b"
integrity sha512-HkCFJnYyFuPaAjk6O8IcafEEBYlHG63SqxgwB0QSyUGwjq/hUekgB23BnKmywbcxNIuX26b9j2AGWt/DBdJlXA==
"@coder/nbin@^1.1.1":
version "1.1.1"
resolved "https://registry.yarnpkg.com/@coder/nbin/-/nbin-1.1.1.tgz#0690928fb1306ee2a84120c8ae8ba221c338b190"
integrity sha512-SDlW0dNw6N5Ge3XlI6nbQV7G7dvTYqxzhN0douJlD56upaU4C130g0FCrhLPU/H4gT3SdZVfWoWc4AGv2fhZZw==
dependencies:
"@coder/logger" "^1.0.3"
fs-extra "^7.0.1"
......
......@@ -10,7 +10,9 @@ const root = path.join(__dirname, "..");
module.exports = (options = {}) => ({
context: root,
devtool: "none",
externals: ["fsevents"],
externals: {
fsevents: "fsevents",
},
module: {
rules: [{
loader: "string-replace-loader",
......@@ -44,48 +46,6 @@ module.exports = (options = {}) => ({
}, {
test: /\.wasm$/,
type: "javascript/auto",
}, {
// Fixes spdlog.
test: /spdlog(\\|\/)index\.js/,
loader: "string-replace-loader",
options: {
multiple: [{
search: "const spdlog.*;",
replace: "const spdlog = __non_webpack_require__(global.SPDLOG_LOCATION);",
flags: "g",
}],
},
}, {
// This is required otherwise it attempts to require("package.json")
test: /@oclif(\\|\/)command(\\|\/)lib(\\|\/)index\.js/,
loader: "string-replace-loader",
options: {
multiple: [{
search: "checkNodeVersion\\(\\);",
replace: "",
flags: "g",
}],
},
}, {
test: /node\-pty\-prebuilt(\\|\/)lib(\\|\/)index\.js/,
loader: "string-replace-loader",
options: {
multiple: [{
search: "exports\\.native.*;",
replace: "exports.native = null;",
flags: "g",
}],
},
}, {
test: /node\-pty\-prebuilt(\\|\/)lib(\\|\/).*\.js/,
loader: "string-replace-loader",
options: {
multiple: [{
search: "var pty = .*pty\.node.*;",
replace: "var pty = __non_webpack_require__(global.NODEPTY_LOCATION);",
flags: "g",
}],
},
}],
},
resolve: {
......
......@@ -5,4 +5,8 @@ module.exports = (options = {}) => merge(
devtool: "none",
mode: "production",
target: "node",
externals: {
spdlog: "commonjs spdlog",
"node-pty": "commonjs node-pty",
}
});
......@@ -23,9 +23,6 @@
],
"vs/*": [
"./lib/vscode/src/vs/*"
],
"node-pty": [
"./packages/protocol/node_modules/node-pty-prebuilt"
]
}
}
......
......@@ -518,6 +518,11 @@ binary-extensions@^1.0.0:
resolved "https://registry.yarnpkg.com/binary-extensions/-/binary-extensions-1.12.0.tgz#c2d780f53d45bba8317a8902d4ceeaf3a6385b14"
integrity sha512-DYWGk01lDcxeS/K9IHPGWfT8PsJmbXRtRd2Sx72Tnb8pcYZQFF1oSDb8hJtS1vhp212q1Rzi5dUf9+nq0o9UIg==
bindings@1.3.0, bindings@^1.3.0:
version "1.3.0"
resolved "https://registry.yarnpkg.com/bindings/-/bindings-1.3.0.tgz#b346f6ecf6a95f5a815c5839fc7cdb22502f1ed7"
integrity sha512-DpLh5EzMR2kzvX1KIlVC0VkC3iZtHKTgdtZ0a3pglBZdaQFjt5S9g9xd1lE+YvXyfd6mtCeRnrUfOLYiTMlNSw==
block-stream@*:
version "0.0.9"
resolved "https://registry.yarnpkg.com/block-stream/-/block-stream-0.0.9.tgz#13ebfe778a03205cfe03751481ebb4b3300c126a"
......@@ -3292,11 +3297,16 @@ multicast-dns@^6.0.1:
dns-packet "^1.3.1"
thunky "^1.0.2"
nan@^2.10.0, nan@^2.9.2:
nan@2.12.1, nan@^2.10.0, nan@^2.9.2:
version "2.12.1"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.12.1.tgz#7b1aa193e9aa86057e3c7bbd0ac448e770925552"
integrity sha512-JY7V6lRkStKcKTvHO5NVSQRv+RV+FIL5pvDoLiAtSL9pKlC5x9PKQcZDsq7m4FO4d57mkhC6Z+QhAh3Jdk5JFw==
nan@^2.8.0:
version "2.13.2"
resolved "https://registry.yarnpkg.com/nan/-/nan-2.13.2.tgz#f51dc7ae66ba7d5d55e1e6d4d8092e802c9aefe7"
integrity sha512-TghvYc72wlMGMVMluVo9WRJc0mB8KxxF/gZ4YYFy7V2ZQX9l7rgbPg7vjS9mt6U5HXODVFVI2bOduCzwOMv/lw==
nanomatch@^1.2.9:
version "1.2.13"
resolved "https://registry.yarnpkg.com/nanomatch/-/nanomatch-1.2.13.tgz#b87a8aa4fc0de8fe6be88895b38983ff265bd119"
......@@ -3418,6 +3428,13 @@ node-pre-gyp@^0.10.0:
semver "^5.3.0"
tar "^4"
node-pty@0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/node-pty/-/node-pty-0.8.1.tgz#94b457bec013e7a09b8d9141f63b0787fa25c23f"
integrity sha512-j+/g0Q5dR+vkELclpJpz32HcS3O/3EdPSGPvDXJZVJQLCvgG0toEbfmymxAEyQyZEpaoKHAcoL+PvKM+4N9nlw==
dependencies:
nan "2.12.1"
node-sass@^4.11.0:
version "4.11.0"
resolved "https://registry.yarnpkg.com/node-sass/-/node-sass-4.11.0.tgz#183faec398e9cbe93ba43362e2768ca988a6369a"
......@@ -4683,6 +4700,15 @@ source-map@^0.6.0, source-map@^0.6.1, source-map@~0.6.0, source-map@~0.6.1:
resolved "https://registry.yarnpkg.com/source-map/-/source-map-0.6.1.tgz#74722af32e9614e9c287a8d0bbde48b5e2f1a263"
integrity sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==
spdlog@0.8.1:
version "0.8.1"
resolved "https://registry.yarnpkg.com/spdlog/-/spdlog-0.8.1.tgz#dfb3f3422ab3efe32be79e4769b95440ed72699f"
integrity sha512-W0s8IOXpn86md+8PJ4mJeB/22thykzH5YaNc3Rgnql4x4/zFIhvNiEx6/a1arnqvmJF0HtRO0Ehlswg0WcwTLQ==
dependencies:
bindings "^1.3.0"
mkdirp "^0.5.1"
nan "^2.8.0"
spdx-correct@^3.0.0:
version "3.1.0"
resolved "https://registry.yarnpkg.com/spdx-correct/-/spdx-correct-3.1.0.tgz#fb83e504445268f154b074e218c87c003cd31df4"
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册