Compare commits
No commits in common. "3cde9a2bf052271551db779bdf573131260677bc" and "78d2a1e560fe1c6b25b3755abdd6f4f53df43287" have entirely different histories.
3cde9a2bf0
...
78d2a1e560
31 changed files with 4589 additions and 1923 deletions
1
.envrc
1
.envrc
|
@ -1 +0,0 @@
|
|||
use flake
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,4 +9,3 @@
|
|||
node_modules
|
||||
dist
|
||||
test
|
||||
.direnv
|
||||
|
|
4
.nlsp-settings/eslint.json
Normal file
4
.nlsp-settings/eslint.json
Normal file
|
@ -0,0 +1,4 @@
|
|||
{
|
||||
"eslint.packageManager": "yarn",
|
||||
"eslint.nodePath": ".yarn/sdks"
|
||||
}
|
3
.nlsp-settings/tsserver.json
Normal file
3
.nlsp-settings/tsserver.json
Normal file
|
@ -0,0 +1,3 @@
|
|||
{
|
||||
"typescript.tsdk": ".yarn/sdks/typescript/lib"
|
||||
}
|
6
.vim/coc-settings.json
Normal file
6
.vim/coc-settings.json
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"workspace.workspaceFolderCheckCwd": false,
|
||||
"tsserver.tsdk": ".yarn/sdks/typescript/lib",
|
||||
"eslint.packageManager": "yarn",
|
||||
"eslint.nodePath": ".yarn/sdks"
|
||||
}
|
6
.vscode/extensions.json
vendored
Normal file
6
.vscode/extensions.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"recommendations": [
|
||||
"arcanis.vscode-zipfs",
|
||||
"dbaeumer.vscode-eslint"
|
||||
]
|
||||
}
|
9
.vscode/settings.json
vendored
Normal file
9
.vscode/settings.json
vendored
Normal file
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"search.exclude": {
|
||||
"**/.yarn": true,
|
||||
"**/.pnp.*": true
|
||||
},
|
||||
"typescript.tsdk": ".yarn/sdks/typescript/lib",
|
||||
"typescript.enablePromptUseWorkspaceTsdk": true,
|
||||
"eslint.nodePath": ".yarn/sdks"
|
||||
}
|
773
.yarn/releases/yarn-sources.cjs
vendored
Executable file
773
.yarn/releases/yarn-sources.cjs
vendored
Executable file
File diff suppressed because one or more lines are too long
20
.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
20
.yarn/sdks/eslint/bin/eslint.js
vendored
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint/bin/eslint.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint/bin/eslint.js your application uses
|
||||
module.exports = absRequire(`eslint/bin/eslint.js`);
|
20
.yarn/sdks/eslint/lib/api.js
vendored
Normal file
20
.yarn/sdks/eslint/lib/api.js
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require eslint
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real eslint your application uses
|
||||
module.exports = absRequire(`eslint`);
|
6
.yarn/sdks/eslint/package.json
vendored
Normal file
6
.yarn/sdks/eslint/package.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "eslint",
|
||||
"version": "8.23.0-sdk",
|
||||
"main": "./lib/api.js",
|
||||
"type": "commonjs"
|
||||
}
|
6
.yarn/sdks/integrations.yml
vendored
Normal file
6
.yarn/sdks/integrations.yml
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
# This file is automatically generated by @yarnpkg/sdks.
|
||||
# Manual changes might be lost!
|
||||
|
||||
integrations:
|
||||
- vscode
|
||||
- vim
|
20
.yarn/sdks/typescript/bin/tsc
vendored
Executable file
20
.yarn/sdks/typescript/bin/tsc
vendored
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsc
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/bin/tsc your application uses
|
||||
module.exports = absRequire(`typescript/bin/tsc`);
|
20
.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
20
.yarn/sdks/typescript/bin/tsserver
vendored
Executable file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/bin/tsserver
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/bin/tsserver your application uses
|
||||
module.exports = absRequire(`typescript/bin/tsserver`);
|
20
.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
20
.yarn/sdks/typescript/lib/tsc.js
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsc.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsc.js your application uses
|
||||
module.exports = absRequire(`typescript/lib/tsc.js`);
|
223
.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
223
.yarn/sdks/typescript/lib/tsserver.js
vendored
Normal file
|
@ -0,0 +1,223 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const {isAbsolute} = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = str => str.startsWith("portal:/");
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
// before forwarding it to TS, and to add it back on all returned paths.
|
||||
|
||||
function toEditorPath(str) {
|
||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||
// We also take the opportunity to turn virtual paths into physical ones;
|
||||
// this makes it much easier to work with workspaces that list peer
|
||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||
// file instances instead of the real ones.
|
||||
//
|
||||
// We only do this to modules owned by the the dependency tree roots.
|
||||
// This avoids breaking the resolution when jumping inside a vendor
|
||||
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||
// errors on react).
|
||||
//
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
str = normalize(str);
|
||||
|
||||
if (str.match(/\.zip\//)) {
|
||||
switch (hostInfo) {
|
||||
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||
// VSCode only adds it automatically for supported schemes,
|
||||
// so we have to do it manually for the `zip` scheme.
|
||||
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||
//
|
||||
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||
//
|
||||
// 2021-10-08: VSCode changed the format in 1.61.
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-04-06: VSCode changed the format in 1.66.
|
||||
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-05-06: VSCode changed the format in 1.68
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Force enable 'allowLocalPluginLoads'
|
||||
// TypeScript tries to resolve plugins using a path relative to itself
|
||||
// which doesn't work when using the global cache
|
||||
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
|
||||
// And here is the point where we hijack the VSCode <-> TS communications
|
||||
// by adding ourselves in the middle. We locate everything that looks
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
onMessage(/** @type {string | object} */ message) {
|
||||
const isStringMessage = typeof message === 'string';
|
||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
typeof parsedMessage === `object` &&
|
||||
parsedMessage.arguments &&
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
hostInfo += ` <1.61`;
|
||||
} else if (minor < 66) {
|
||||
hostInfo += ` <1.66`;
|
||||
} else if (minor < 68) {
|
||||
hostInfo += ` <1.68`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
});
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||
);
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
};
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserver.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserver.js your application uses
|
||||
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserver.js`));
|
223
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
223
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
Normal file
|
@ -0,0 +1,223 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
const moduleWrapper = tsserver => {
|
||||
if (!process.versions.pnp) {
|
||||
return tsserver;
|
||||
}
|
||||
|
||||
const {isAbsolute} = require(`path`);
|
||||
const pnpApi = require(`pnpapi`);
|
||||
|
||||
const isVirtual = str => str.match(/\/(\$\$virtual|__virtual__)\//);
|
||||
const isPortal = str => str.startsWith("portal:/");
|
||||
const normalize = str => str.replace(/\\/g, `/`).replace(/^\/?/, `/`);
|
||||
|
||||
const dependencyTreeRoots = new Set(pnpApi.getDependencyTreeRoots().map(locator => {
|
||||
return `${locator.name}@${locator.reference}`;
|
||||
}));
|
||||
|
||||
// VSCode sends the zip paths to TS using the "zip://" prefix, that TS
|
||||
// doesn't understand. This layer makes sure to remove the protocol
|
||||
// before forwarding it to TS, and to add it back on all returned paths.
|
||||
|
||||
function toEditorPath(str) {
|
||||
// We add the `zip:` prefix to both `.zip/` paths and virtual paths
|
||||
if (isAbsolute(str) && !str.match(/^\^?(zip:|\/zip\/)/) && (str.match(/\.zip\//) || isVirtual(str))) {
|
||||
// We also take the opportunity to turn virtual paths into physical ones;
|
||||
// this makes it much easier to work with workspaces that list peer
|
||||
// dependencies, since otherwise Ctrl+Click would bring us to the virtual
|
||||
// file instances instead of the real ones.
|
||||
//
|
||||
// We only do this to modules owned by the the dependency tree roots.
|
||||
// This avoids breaking the resolution when jumping inside a vendor
|
||||
// with peer dep (otherwise jumping into react-dom would show resolution
|
||||
// errors on react).
|
||||
//
|
||||
const resolved = isVirtual(str) ? pnpApi.resolveVirtual(str) : str;
|
||||
if (resolved) {
|
||||
const locator = pnpApi.findPackageLocator(resolved);
|
||||
if (locator && (dependencyTreeRoots.has(`${locator.name}@${locator.reference}`) || isPortal(locator.reference))) {
|
||||
str = resolved;
|
||||
}
|
||||
}
|
||||
|
||||
str = normalize(str);
|
||||
|
||||
if (str.match(/\.zip\//)) {
|
||||
switch (hostInfo) {
|
||||
// Absolute VSCode `Uri.fsPath`s need to start with a slash.
|
||||
// VSCode only adds it automatically for supported schemes,
|
||||
// so we have to do it manually for the `zip` scheme.
|
||||
// The path needs to start with a caret otherwise VSCode doesn't handle the protocol
|
||||
//
|
||||
// Ref: https://github.com/microsoft/vscode/issues/105014#issuecomment-686760910
|
||||
//
|
||||
// 2021-10-08: VSCode changed the format in 1.61.
|
||||
// Before | ^zip:/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-04-06: VSCode changed the format in 1.66.
|
||||
// Before | ^/zip//c:/foo/bar.zip/package.json
|
||||
// After | ^/zip/c:/foo/bar.zip/package.json
|
||||
//
|
||||
// 2022-05-06: VSCode changed the format in 1.68
|
||||
// Before | ^/zip/c:/foo/bar.zip/package.json
|
||||
// After | ^/zip//c:/foo/bar.zip/package.json
|
||||
//
|
||||
case `vscode <1.61`: {
|
||||
str = `^zip:${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.66`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode <1.68`: {
|
||||
str = `^/zip${str}`;
|
||||
} break;
|
||||
|
||||
case `vscode`: {
|
||||
str = `^/zip/${str}`;
|
||||
} break;
|
||||
|
||||
// To make "go to definition" work,
|
||||
// We have to resolve the actual file system path from virtual path
|
||||
// and convert scheme to supported by [vim-rzip](https://github.com/lbrayner/vim-rzip)
|
||||
case `coc-nvim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = resolve(`zipfile:${str}`);
|
||||
} break;
|
||||
|
||||
// Support neovim native LSP and [typescript-language-server](https://github.com/theia-ide/typescript-language-server)
|
||||
// We have to resolve the actual file system path from virtual path,
|
||||
// everything else is up to neovim
|
||||
case `neovim`: {
|
||||
str = normalize(resolved).replace(/\.zip\//, `.zip::`);
|
||||
str = `zipfile://${str}`;
|
||||
} break;
|
||||
|
||||
default: {
|
||||
str = `zip:${str}`;
|
||||
} break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return str;
|
||||
}
|
||||
|
||||
function fromEditorPath(str) {
|
||||
switch (hostInfo) {
|
||||
case `coc-nvim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for coc-nvim is in format of /<pwd>/zipfile:/<pwd>/.yarn/...
|
||||
// So in order to convert it back, we use .* to match all the thing
|
||||
// before `zipfile:`
|
||||
return process.platform === `win32`
|
||||
? str.replace(/^.*zipfile:\//, ``)
|
||||
: str.replace(/^.*zipfile:/, ``);
|
||||
} break;
|
||||
|
||||
case `neovim`: {
|
||||
str = str.replace(/\.zip::/, `.zip/`);
|
||||
// The path for neovim is in format of zipfile:///<pwd>/.yarn/...
|
||||
return str.replace(/^zipfile:\/\//, ``);
|
||||
} break;
|
||||
|
||||
case `vscode`:
|
||||
default: {
|
||||
return str.replace(/^\^?(zip:|\/zip(\/ts-nul-authority)?)\/+/, process.platform === `win32` ? `` : `/`)
|
||||
} break;
|
||||
}
|
||||
}
|
||||
|
||||
// Force enable 'allowLocalPluginLoads'
|
||||
// TypeScript tries to resolve plugins using a path relative to itself
|
||||
// which doesn't work when using the global cache
|
||||
// https://github.com/microsoft/TypeScript/blob/1b57a0395e0bff191581c9606aab92832001de62/src/server/project.ts#L2238
|
||||
// VSCode doesn't want to enable 'allowLocalPluginLoads' due to security concerns but
|
||||
// TypeScript already does local loads and if this code is running the user trusts the workspace
|
||||
// https://github.com/microsoft/vscode/issues/45856
|
||||
const ConfiguredProject = tsserver.server.ConfiguredProject;
|
||||
const {enablePluginsWithOptions: originalEnablePluginsWithOptions} = ConfiguredProject.prototype;
|
||||
ConfiguredProject.prototype.enablePluginsWithOptions = function() {
|
||||
this.projectService.allowLocalPluginLoads = true;
|
||||
return originalEnablePluginsWithOptions.apply(this, arguments);
|
||||
};
|
||||
|
||||
// And here is the point where we hijack the VSCode <-> TS communications
|
||||
// by adding ourselves in the middle. We locate everything that looks
|
||||
// like an absolute path of ours and normalize it.
|
||||
|
||||
const Session = tsserver.server.Session;
|
||||
const {onMessage: originalOnMessage, send: originalSend} = Session.prototype;
|
||||
let hostInfo = `unknown`;
|
||||
|
||||
Object.assign(Session.prototype, {
|
||||
onMessage(/** @type {string | object} */ message) {
|
||||
const isStringMessage = typeof message === 'string';
|
||||
const parsedMessage = isStringMessage ? JSON.parse(message) : message;
|
||||
|
||||
if (
|
||||
parsedMessage != null &&
|
||||
typeof parsedMessage === `object` &&
|
||||
parsedMessage.arguments &&
|
||||
typeof parsedMessage.arguments.hostInfo === `string`
|
||||
) {
|
||||
hostInfo = parsedMessage.arguments.hostInfo;
|
||||
if (hostInfo === `vscode` && process.env.VSCODE_IPC_HOOK) {
|
||||
const [, major, minor] = (process.env.VSCODE_IPC_HOOK.match(
|
||||
// The RegExp from https://semver.org/ but without the caret at the start
|
||||
/(0|[1-9]\d*)\.(0|[1-9]\d*)\.(0|[1-9]\d*)(?:-((?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+([0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$/
|
||||
) ?? []).map(Number)
|
||||
|
||||
if (major === 1) {
|
||||
if (minor < 61) {
|
||||
hostInfo += ` <1.61`;
|
||||
} else if (minor < 66) {
|
||||
hostInfo += ` <1.66`;
|
||||
} else if (minor < 68) {
|
||||
hostInfo += ` <1.68`;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
const processedMessageJSON = JSON.stringify(parsedMessage, (key, value) => {
|
||||
return typeof value === 'string' ? fromEditorPath(value) : value;
|
||||
});
|
||||
|
||||
return originalOnMessage.call(
|
||||
this,
|
||||
isStringMessage ? processedMessageJSON : JSON.parse(processedMessageJSON)
|
||||
);
|
||||
},
|
||||
|
||||
send(/** @type {any} */ msg) {
|
||||
return originalSend.call(this, JSON.parse(JSON.stringify(msg, (key, value) => {
|
||||
return typeof value === `string` ? toEditorPath(value) : value;
|
||||
})));
|
||||
}
|
||||
});
|
||||
|
||||
return tsserver;
|
||||
};
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/tsserverlibrary.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/tsserverlibrary.js your application uses
|
||||
module.exports = moduleWrapper(absRequire(`typescript/lib/tsserverlibrary.js`));
|
20
.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
20
.yarn/sdks/typescript/lib/typescript.js
vendored
Normal file
|
@ -0,0 +1,20 @@
|
|||
#!/usr/bin/env node
|
||||
|
||||
const {existsSync} = require(`fs`);
|
||||
const {createRequire} = require(`module`);
|
||||
const {resolve} = require(`path`);
|
||||
|
||||
const relPnpApiPath = "../../../../.pnp.cjs";
|
||||
|
||||
const absPnpApiPath = resolve(__dirname, relPnpApiPath);
|
||||
const absRequire = createRequire(absPnpApiPath);
|
||||
|
||||
if (existsSync(absPnpApiPath)) {
|
||||
if (!process.versions.pnp) {
|
||||
// Setup the environment to be able to require typescript/lib/typescript.js
|
||||
require(absPnpApiPath).setup();
|
||||
}
|
||||
}
|
||||
|
||||
// Defer to the real typescript/lib/typescript.js your application uses
|
||||
module.exports = absRequire(`typescript/lib/typescript.js`);
|
6
.yarn/sdks/typescript/package.json
vendored
Normal file
6
.yarn/sdks/typescript/package.json
vendored
Normal file
|
@ -0,0 +1,6 @@
|
|||
{
|
||||
"name": "typescript",
|
||||
"version": "4.8.2-sdk",
|
||||
"main": "./lib/typescript.js",
|
||||
"type": "commonjs"
|
||||
}
|
1
.yarnrc.yml
Normal file
1
.yarnrc.yml
Normal file
|
@ -0,0 +1 @@
|
|||
yarnPath: .yarn/releases/yarn-sources.cjs
|
26
flake.lock
26
flake.lock
|
@ -1,26 +0,0 @@
|
|||
{
|
||||
"nodes": {
|
||||
"nixpkgs": {
|
||||
"locked": {
|
||||
"lastModified": 1674030803,
|
||||
"narHash": "sha256-ZHMsgrsqOeURn1+WO4hPA22C3oH3x0zp8xqU5temz+U=",
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"rev": "84341d7769f6a3b6b43051774c0c6df4f5df6d15",
|
||||
"type": "github"
|
||||
},
|
||||
"original": {
|
||||
"owner": "NixOS",
|
||||
"repo": "nixpkgs",
|
||||
"type": "github"
|
||||
}
|
||||
},
|
||||
"root": {
|
||||
"inputs": {
|
||||
"nixpkgs": "nixpkgs"
|
||||
}
|
||||
}
|
||||
},
|
||||
"root": "root",
|
||||
"version": 7
|
||||
}
|
21
flake.nix
21
flake.nix
|
@ -1,21 +0,0 @@
|
|||
{
|
||||
description = "Linux Package Manager";
|
||||
|
||||
inputs = {
|
||||
nixpkgs.url = "github:NixOS/nixpkgs";
|
||||
};
|
||||
|
||||
outputs = {
|
||||
self,
|
||||
nixpkgs,
|
||||
...
|
||||
}: let
|
||||
pkgs = nixpkgs.legacyPackages.x86_64-linux;
|
||||
in {
|
||||
devShell.x86_64-linux = pkgs.mkShell {
|
||||
packages = [pkgs.nodePackages.pnpm pkgs.nodejs-18_x];
|
||||
};
|
||||
|
||||
formatter.x86_64-linux = pkgs.alejandra;
|
||||
};
|
||||
}
|
41
package.json
41
package.json
|
@ -7,29 +7,30 @@
|
|||
"license": "GPL-3.0-or-later",
|
||||
"bin": "./dist/index.js",
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"lint": "eslint --fix src/**/*.ts",
|
||||
"package:alpine": "pkg -t node18-alpine-x64 -C brotli -o dist/wahpkg dist/index.js",
|
||||
"package:linux": "pkg -t node18-linux-x64 -C brotli -o dist/wahpkg dist/index.js"
|
||||
"build": "rollup -c",
|
||||
"lint": "eslint --fix src/**/*.ts"
|
||||
},
|
||||
"dependencies": {
|
||||
"chalk": "^5.2.0",
|
||||
"commander": "^9.5.0",
|
||||
"fs-extra": "^11.1.0",
|
||||
"lowdb": "^5.0.5",
|
||||
"proper-lockfile": "^4.1.2",
|
||||
"tar": "^6.1.13",
|
||||
"type-fest": "^3.5.1"
|
||||
"chalk": "^5.0.1",
|
||||
"commander": "^9.4.0",
|
||||
"fs-extra": "^10.1.0",
|
||||
"lowdb": "^3.0.0",
|
||||
"tar": "^6.1.11",
|
||||
"type-fest": "^2.19.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@rushstack/eslint-patch": "^1.2.0",
|
||||
"@types/fs-extra": "^11.0.1",
|
||||
"@types/proper-lockfile": "^4.1.2",
|
||||
"@types/tar": "^6.1.3",
|
||||
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
||||
"@typescript-eslint/parser": "^5.48.1",
|
||||
"eslint": "^8.31.0",
|
||||
"pkg": "^5.8.0",
|
||||
"typescript": "^4.9.4"
|
||||
"@rollup/plugin-commonjs": "^22.0.2",
|
||||
"@rollup/plugin-node-resolve": "^14.0.0",
|
||||
"@rushstack/eslint-patch": "^1.1.4",
|
||||
"@types/fs-extra": "^9.0.13",
|
||||
"@types/tar": "^6.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
||||
"@typescript-eslint/parser": "^5.36.2",
|
||||
"@yarnpkg/sdks": "^3.0.0-rc.18",
|
||||
"eslint": "^8.23.0",
|
||||
"rollup": "^2.79.0",
|
||||
"rollup-plugin-terser": "^7.0.2",
|
||||
"rollup-plugin-typescript2": "^0.33.0",
|
||||
"typescript": "^4.8.2"
|
||||
}
|
||||
}
|
||||
|
|
1782
pnpm-lock.yaml
1782
pnpm-lock.yaml
File diff suppressed because it is too large
Load diff
18
rollup.config.js
Normal file
18
rollup.config.js
Normal file
|
@ -0,0 +1,18 @@
|
|||
import commonjs from '@rollup/plugin-commonjs';
|
||||
import { nodeResolve } from '@rollup/plugin-node-resolve';
|
||||
import { terser } from 'rollup-plugin-terser';
|
||||
import typescript from 'rollup-plugin-typescript2';
|
||||
|
||||
export default {
|
||||
input: 'src/index.ts',
|
||||
output: {
|
||||
dir: 'dist',
|
||||
format: 'esm',
|
||||
},
|
||||
plugins: [
|
||||
commonjs(),
|
||||
nodeResolve({ exportConditions: ['node'] }),
|
||||
typescript(),
|
||||
terser(),
|
||||
],
|
||||
};
|
|
@ -1,15 +1,13 @@
|
|||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import { Low } from 'lowdb';
|
||||
import { JSONFile, TextFile } from 'lowdb/node';
|
||||
import * as lowdb from 'lowdb';
|
||||
import crypto from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import os from 'node:os';
|
||||
import { lock, unlock } from 'proper-lockfile';
|
||||
import tar from 'tar';
|
||||
|
||||
import { GlobalOptions } from '../index.js';
|
||||
import { checkLockFile, makeDbFile, parseWahInfo, walk } from '../utils.js';
|
||||
import { makeDbFile, parseWahInfo, walk } from '../utils.js';
|
||||
|
||||
export default class Install {
|
||||
public name: string;
|
||||
|
@ -29,9 +27,6 @@ export default class Install {
|
|||
}
|
||||
|
||||
await makeDbFile(this.options.sysroot);
|
||||
await checkLockFile(this.options.sysroot);
|
||||
|
||||
await lock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg.lock` });
|
||||
|
||||
if (!await fs.pathExists(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg'))) await fs.mkdirp(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg'));
|
||||
const makeExtractDir = await fs.mkdtemp(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg', `${path.basename(this.options.file)}-`));
|
||||
|
@ -49,8 +44,8 @@ export default class Install {
|
|||
const getWahInfo = parseWahInfo(readWahInfo.toString());
|
||||
|
||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||
const adapter = new JSONFile<dbData>(dbFile);
|
||||
const db = new Low(adapter);
|
||||
const adapter = new lowdb.JSONFile<dbData>(dbFile);
|
||||
const db = new lowdb.Low(adapter);
|
||||
|
||||
await db.read();
|
||||
|
||||
|
@ -67,7 +62,7 @@ export default class Install {
|
|||
|
||||
await fs.mkdirp(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${getWahInfo.name}`);
|
||||
await fs.writeFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${getWahInfo.name}/MD5HASHES`, '');
|
||||
const md5Adapter = new TextFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${getWahInfo.name}/MD5HASHES`);
|
||||
const md5Adapter = new lowdb.TextFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${getWahInfo.name}/MD5HASHES`);
|
||||
|
||||
await md5Adapter.read();
|
||||
|
||||
|
@ -82,13 +77,10 @@ export default class Install {
|
|||
|
||||
await md5Adapter.write(toWriteToFile);
|
||||
|
||||
await fs.copy(path.join(makeExtractDir, 'ROOT'), path.join(this.options.sysroot), { overwrite: true, dereference: false, preserveTimestamps: true });
|
||||
await fs.copy(path.join(makeExtractDir, 'ROOT'), path.join(this.options.sysroot), { overwrite: true, dereference: false, preserveTimestamps: true, recursive: true });
|
||||
await fs.rm(path.join(makeExtractDir), { recursive: true, force: true });
|
||||
|
||||
console.log(chalk.bold.green`Package %s has been installed!`, getWahInfo.name);
|
||||
|
||||
await unlock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg.lock` });
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
import { Low } from 'lowdb';
|
||||
import { JSONFile } from 'lowdb/node';
|
||||
import * as lowdb from 'lowdb';
|
||||
|
||||
import { GlobalOptions } from '../index.js';
|
||||
import { makeDbFile } from '../utils.js';
|
||||
|
@ -12,12 +11,13 @@ export default class List {
|
|||
this.options = options;
|
||||
}
|
||||
|
||||
|
||||
public async run(): Promise<void> {
|
||||
await makeDbFile(this.options.sysroot);
|
||||
|
||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||
const adapter = new JSONFile<dbData>(dbFile);
|
||||
const db = new Low(adapter);
|
||||
const adapter = new lowdb.JSONFile<dbData>(dbFile);
|
||||
const db = new lowdb.Low(adapter);
|
||||
|
||||
await db.read();
|
||||
|
||||
|
|
|
@ -1,13 +1,11 @@
|
|||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import { Low } from 'lowdb';
|
||||
import { JSONFile } from 'lowdb/node';
|
||||
import * as lowdb from 'lowdb';
|
||||
import crypto from 'node:crypto';
|
||||
import path from 'node:path';
|
||||
import { lock, unlock } from 'proper-lockfile';
|
||||
|
||||
import { GlobalOptions } from '../index.js';
|
||||
import { checkLockFile, makeDbFile } from '../utils.js';
|
||||
import { makeDbFile } from '../utils.js';
|
||||
import { dbData } from './install.js';
|
||||
|
||||
export default class Uninstall {
|
||||
|
@ -21,50 +19,46 @@ export default class Uninstall {
|
|||
|
||||
public async run(): Promise<void> {
|
||||
await makeDbFile(this.options.sysroot);
|
||||
await checkLockFile(this.options.sysroot);
|
||||
|
||||
await lock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg/wahpkg.lock` });
|
||||
if (this.name) {
|
||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||
const adapter = new lowdb.JSONFile<dbData>(dbFile);
|
||||
const db = new lowdb.Low(adapter);
|
||||
|
||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||
const adapter = new JSONFile<dbData>(dbFile);
|
||||
const db = new Low(adapter);
|
||||
await db.read();
|
||||
|
||||
await db.read();
|
||||
|
||||
if (!db.data.pkgs.find((val) => val.name === this.name)) {
|
||||
console.error(chalk.red.bold`%s is not installed!`, this.name);
|
||||
process.exit(1);
|
||||
}
|
||||
|
||||
const md5File = await fs.readFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}/MD5HASHES`);
|
||||
|
||||
for (const file of md5File.toString().split('\n')) {
|
||||
const fileName = file.split(' ')[0];
|
||||
const hash = file.split(' ')[1];
|
||||
|
||||
// Ignore blank or last line
|
||||
if (fileName === '') continue;
|
||||
|
||||
const verifyHash = crypto.createHash('md5').update(await fs.readFile(path.join(this.options.sysroot, fileName))).digest('hex');
|
||||
|
||||
if (verifyHash !== hash && this.options['ignore-hash']) {
|
||||
console.error(chalk.red.bold`%s does not match MD5 hash of %s! To ignore this error, please add --ignore-hash in the command`, this.name, hash);
|
||||
if (!db.data.pkgs.find((val) => val.name === this.name)) {
|
||||
console.error(chalk.red.bold`%s is not installed!`, this.name);
|
||||
process.exit(1);
|
||||
} else {
|
||||
await fs.rm(path.join(this.options.sysroot, fileName));
|
||||
}
|
||||
|
||||
const md5File = await fs.readFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}/MD5HASHES`);
|
||||
|
||||
for (const file of md5File.toString().split('\n')) {
|
||||
const fileName = file.split(' ')[0];
|
||||
const hash = file.split(' ')[1];
|
||||
|
||||
// Ignore blank or last line
|
||||
if (fileName === '') continue;
|
||||
|
||||
const verifyHash = crypto.createHash('md5').update(await fs.readFile(path.join(this.options.sysroot, fileName))).digest('hex');
|
||||
|
||||
if (verifyHash !== hash && this.options['ignore-hash']) {
|
||||
console.error(chalk.red.bold`%s does not match MD5 hash of %s! To ignore this error, please add --ignore-hash in the command`, this.name, hash);
|
||||
process.exit(1);
|
||||
} else {
|
||||
await fs.rm(path.join(this.options.sysroot, fileName));
|
||||
}
|
||||
}
|
||||
|
||||
await fs.rm(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}`, { force: true, recursive: true });
|
||||
|
||||
db.data.pkgs = db.data.pkgs.filter((item) => item.name !== this.name);
|
||||
await db.write();
|
||||
|
||||
console.log(chalk.green.bold`%s has been successfully removed!`, this.name);
|
||||
process.exit(0);
|
||||
}
|
||||
|
||||
await fs.rm(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${this.name}`, { force: true, recursive: true });
|
||||
|
||||
db.data.pkgs = db.data.pkgs.filter((item) => item.name !== this.name);
|
||||
await db.write();
|
||||
|
||||
console.log(chalk.green.bold`%s has been successfully removed!`, this.name);
|
||||
|
||||
await unlock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg/wahpkg.lock` });
|
||||
|
||||
process.exit(0);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -1,7 +1,6 @@
|
|||
import chalk from 'chalk';
|
||||
import fs from 'fs-extra';
|
||||
import path from 'node:path';
|
||||
import { check, lock } from 'proper-lockfile';
|
||||
import type { PartialDeep, RequireAllOrNone } from 'type-fest';
|
||||
|
||||
export function parseWahInfo(file: string): WahInfo {
|
||||
|
@ -73,10 +72,6 @@ export const makeDbFile = async (sysroot: string): Promise<void> => {
|
|||
}
|
||||
};
|
||||
|
||||
export const checkLockFile = async (sysroot: string): Promise<boolean> => {
|
||||
return await check(`${sysroot}/var/lib/wahpkg`, { lockfilePath: `${sysroot}/var/lib/wahpkg/wahpkg.lock` });
|
||||
};
|
||||
|
||||
export interface WahInfo {
|
||||
name: string;
|
||||
version: {
|
||||
|
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"compilerOptions": {
|
||||
"module": "ES2022",
|
||||
"module": "ESNext",
|
||||
"moduleResolution": "NodeNext",
|
||||
"lib": [
|
||||
"ES2022",
|
||||
|
@ -8,10 +8,6 @@
|
|||
],
|
||||
"target": "ES2022",
|
||||
"allowSyntheticDefaultImports": true,
|
||||
"esModuleInterop": true,
|
||||
"outDir": "dist"
|
||||
},
|
||||
"include": [
|
||||
"src/**/*.ts"
|
||||
]
|
||||
"esModuleInterop": true
|
||||
}
|
||||
}
|
||||
|
|
Reference in a new issue