Compare commits
10 commits
78d2a1e560
...
3cde9a2bf0
Author | SHA1 | Date | |
---|---|---|---|
3cde9a2bf0 | |||
e703b0b4fe | |||
e50b5c2e78 | |||
0121666f8c | |||
5910abdf6d | |||
3d19743d8d | |||
10b690c8ce | |||
cbb3e40e14 | |||
177b030ff8 | |||
b1bf4ad3fe |
31 changed files with 1924 additions and 4590 deletions
1
.envrc
Normal file
1
.envrc
Normal file
|
@ -0,0 +1 @@
|
||||||
|
use flake
|
1
.gitignore
vendored
1
.gitignore
vendored
|
@ -9,3 +9,4 @@
|
||||||
node_modules
|
node_modules
|
||||||
dist
|
dist
|
||||||
test
|
test
|
||||||
|
.direnv
|
||||||
|
|
|
@ -1,4 +0,0 @@
|
||||||
{
|
|
||||||
"eslint.packageManager": "yarn",
|
|
||||||
"eslint.nodePath": ".yarn/sdks"
|
|
||||||
}
|
|
|
@ -1,3 +0,0 @@
|
||||||
{
|
|
||||||
"typescript.tsdk": ".yarn/sdks/typescript/lib"
|
|
||||||
}
|
|
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"workspace.workspaceFolderCheckCwd": false,
|
|
||||||
"tsserver.tsdk": ".yarn/sdks/typescript/lib",
|
|
||||||
"eslint.packageManager": "yarn",
|
|
||||||
"eslint.nodePath": ".yarn/sdks"
|
|
||||||
}
|
|
6
.vscode/extensions.json
vendored
6
.vscode/extensions.json
vendored
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"recommendations": [
|
|
||||||
"arcanis.vscode-zipfs",
|
|
||||||
"dbaeumer.vscode-eslint"
|
|
||||||
]
|
|
||||||
}
|
|
9
.vscode/settings.json
vendored
9
.vscode/settings.json
vendored
|
@ -1,9 +0,0 @@
|
||||||
{
|
|
||||||
"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
773
.yarn/releases/yarn-sources.cjs
vendored
File diff suppressed because one or more lines are too long
20
.yarn/sdks/eslint/bin/eslint.js
vendored
20
.yarn/sdks/eslint/bin/eslint.js
vendored
|
@ -1,20 +0,0 @@
|
||||||
#!/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
20
.yarn/sdks/eslint/lib/api.js
vendored
|
@ -1,20 +0,0 @@
|
||||||
#!/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
6
.yarn/sdks/eslint/package.json
vendored
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"name": "eslint",
|
|
||||||
"version": "8.23.0-sdk",
|
|
||||||
"main": "./lib/api.js",
|
|
||||||
"type": "commonjs"
|
|
||||||
}
|
|
6
.yarn/sdks/integrations.yml
vendored
6
.yarn/sdks/integrations.yml
vendored
|
@ -1,6 +0,0 @@
|
||||||
# This file is automatically generated by @yarnpkg/sdks.
|
|
||||||
# Manual changes might be lost!
|
|
||||||
|
|
||||||
integrations:
|
|
||||||
- vscode
|
|
||||||
- vim
|
|
20
.yarn/sdks/typescript/bin/tsc
vendored
20
.yarn/sdks/typescript/bin/tsc
vendored
|
@ -1,20 +0,0 @@
|
||||||
#!/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
20
.yarn/sdks/typescript/bin/tsserver
vendored
|
@ -1,20 +0,0 @@
|
||||||
#!/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
20
.yarn/sdks/typescript/lib/tsc.js
vendored
|
@ -1,20 +0,0 @@
|
||||||
#!/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
223
.yarn/sdks/typescript/lib/tsserver.js
vendored
|
@ -1,223 +0,0 @@
|
||||||
#!/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
223
.yarn/sdks/typescript/lib/tsserverlibrary.js
vendored
|
@ -1,223 +0,0 @@
|
||||||
#!/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
20
.yarn/sdks/typescript/lib/typescript.js
vendored
|
@ -1,20 +0,0 @@
|
||||||
#!/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
6
.yarn/sdks/typescript/package.json
vendored
|
@ -1,6 +0,0 @@
|
||||||
{
|
|
||||||
"name": "typescript",
|
|
||||||
"version": "4.8.2-sdk",
|
|
||||||
"main": "./lib/typescript.js",
|
|
||||||
"type": "commonjs"
|
|
||||||
}
|
|
|
@ -1 +0,0 @@
|
||||||
yarnPath: .yarn/releases/yarn-sources.cjs
|
|
26
flake.lock
Normal file
26
flake.lock
Normal file
|
@ -0,0 +1,26 @@
|
||||||
|
{
|
||||||
|
"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
Normal file
21
flake.nix
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
{
|
||||||
|
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,30 +7,29 @@
|
||||||
"license": "GPL-3.0-or-later",
|
"license": "GPL-3.0-or-later",
|
||||||
"bin": "./dist/index.js",
|
"bin": "./dist/index.js",
|
||||||
"scripts": {
|
"scripts": {
|
||||||
"build": "rollup -c",
|
"build": "tsc",
|
||||||
"lint": "eslint --fix src/**/*.ts"
|
"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"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
"chalk": "^5.0.1",
|
"chalk": "^5.2.0",
|
||||||
"commander": "^9.4.0",
|
"commander": "^9.5.0",
|
||||||
"fs-extra": "^10.1.0",
|
"fs-extra": "^11.1.0",
|
||||||
"lowdb": "^3.0.0",
|
"lowdb": "^5.0.5",
|
||||||
"tar": "^6.1.11",
|
"proper-lockfile": "^4.1.2",
|
||||||
"type-fest": "^2.19.0"
|
"tar": "^6.1.13",
|
||||||
|
"type-fest": "^3.5.1"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@rollup/plugin-commonjs": "^22.0.2",
|
"@rushstack/eslint-patch": "^1.2.0",
|
||||||
"@rollup/plugin-node-resolve": "^14.0.0",
|
"@types/fs-extra": "^11.0.1",
|
||||||
"@rushstack/eslint-patch": "^1.1.4",
|
"@types/proper-lockfile": "^4.1.2",
|
||||||
"@types/fs-extra": "^9.0.13",
|
"@types/tar": "^6.1.3",
|
||||||
"@types/tar": "^6.1.2",
|
"@typescript-eslint/eslint-plugin": "^5.48.1",
|
||||||
"@typescript-eslint/eslint-plugin": "^5.36.2",
|
"@typescript-eslint/parser": "^5.48.1",
|
||||||
"@typescript-eslint/parser": "^5.36.2",
|
"eslint": "^8.31.0",
|
||||||
"@yarnpkg/sdks": "^3.0.0-rc.18",
|
"pkg": "^5.8.0",
|
||||||
"eslint": "^8.23.0",
|
"typescript": "^4.9.4"
|
||||||
"rollup": "^2.79.0",
|
|
||||||
"rollup-plugin-terser": "^7.0.2",
|
|
||||||
"rollup-plugin-typescript2": "^0.33.0",
|
|
||||||
"typescript": "^4.8.2"
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
1782
pnpm-lock.yaml
Normal file
1782
pnpm-lock.yaml
Normal file
File diff suppressed because it is too large
Load diff
|
@ -1,18 +0,0 @@
|
||||||
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,13 +1,15 @@
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import * as lowdb from 'lowdb';
|
import { Low } from 'lowdb';
|
||||||
|
import { JSONFile, TextFile } from 'lowdb/node';
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
import os from 'node:os';
|
import os from 'node:os';
|
||||||
|
import { lock, unlock } from 'proper-lockfile';
|
||||||
import tar from 'tar';
|
import tar from 'tar';
|
||||||
|
|
||||||
import { GlobalOptions } from '../index.js';
|
import { GlobalOptions } from '../index.js';
|
||||||
import { makeDbFile, parseWahInfo, walk } from '../utils.js';
|
import { checkLockFile, makeDbFile, parseWahInfo, walk } from '../utils.js';
|
||||||
|
|
||||||
export default class Install {
|
export default class Install {
|
||||||
public name: string;
|
public name: string;
|
||||||
|
@ -27,6 +29,9 @@ export default class Install {
|
||||||
}
|
}
|
||||||
|
|
||||||
await makeDbFile(this.options.sysroot);
|
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'));
|
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)}-`));
|
const makeExtractDir = await fs.mkdtemp(path.join(this.options.sysroot, os.tmpdir(), 'wahpkg', `${path.basename(this.options.file)}-`));
|
||||||
|
@ -44,8 +49,8 @@ export default class Install {
|
||||||
const getWahInfo = parseWahInfo(readWahInfo.toString());
|
const getWahInfo = parseWahInfo(readWahInfo.toString());
|
||||||
|
|
||||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||||
const adapter = new lowdb.JSONFile<dbData>(dbFile);
|
const adapter = new JSONFile<dbData>(dbFile);
|
||||||
const db = new lowdb.Low(adapter);
|
const db = new Low(adapter);
|
||||||
|
|
||||||
await db.read();
|
await db.read();
|
||||||
|
|
||||||
|
@ -62,7 +67,7 @@ export default class Install {
|
||||||
|
|
||||||
await fs.mkdirp(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${getWahInfo.name}`);
|
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`, '');
|
await fs.writeFile(`${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`);
|
const md5Adapter = new TextFile(`${this.options.sysroot}/var/lib/wahpkg/pkgs/${getWahInfo.name}/MD5HASHES`);
|
||||||
|
|
||||||
await md5Adapter.read();
|
await md5Adapter.read();
|
||||||
|
|
||||||
|
@ -77,10 +82,13 @@ export default class Install {
|
||||||
|
|
||||||
await md5Adapter.write(toWriteToFile);
|
await md5Adapter.write(toWriteToFile);
|
||||||
|
|
||||||
await fs.copy(path.join(makeExtractDir, 'ROOT'), path.join(this.options.sysroot), { overwrite: true, dereference: false, preserveTimestamps: true, recursive: true });
|
await fs.copy(path.join(makeExtractDir, 'ROOT'), path.join(this.options.sysroot), { overwrite: true, dereference: false, preserveTimestamps: true });
|
||||||
await fs.rm(path.join(makeExtractDir), { recursive: true, force: true });
|
await fs.rm(path.join(makeExtractDir), { recursive: true, force: true });
|
||||||
|
|
||||||
console.log(chalk.bold.green`Package %s has been installed!`, getWahInfo.name);
|
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);
|
process.exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,4 +1,5 @@
|
||||||
import * as lowdb from 'lowdb';
|
import { Low } from 'lowdb';
|
||||||
|
import { JSONFile } from 'lowdb/node';
|
||||||
|
|
||||||
import { GlobalOptions } from '../index.js';
|
import { GlobalOptions } from '../index.js';
|
||||||
import { makeDbFile } from '../utils.js';
|
import { makeDbFile } from '../utils.js';
|
||||||
|
@ -11,13 +12,12 @@ export default class List {
|
||||||
this.options = options;
|
this.options = options;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public async run(): Promise<void> {
|
public async run(): Promise<void> {
|
||||||
await makeDbFile(this.options.sysroot);
|
await makeDbFile(this.options.sysroot);
|
||||||
|
|
||||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||||
const adapter = new lowdb.JSONFile<dbData>(dbFile);
|
const adapter = new JSONFile<dbData>(dbFile);
|
||||||
const db = new lowdb.Low(adapter);
|
const db = new Low(adapter);
|
||||||
|
|
||||||
await db.read();
|
await db.read();
|
||||||
|
|
||||||
|
|
|
@ -1,11 +1,13 @@
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import * as lowdb from 'lowdb';
|
import { Low } from 'lowdb';
|
||||||
|
import { JSONFile } from 'lowdb/node';
|
||||||
import crypto from 'node:crypto';
|
import crypto from 'node:crypto';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import { lock, unlock } from 'proper-lockfile';
|
||||||
|
|
||||||
import { GlobalOptions } from '../index.js';
|
import { GlobalOptions } from '../index.js';
|
||||||
import { makeDbFile } from '../utils.js';
|
import { checkLockFile, makeDbFile } from '../utils.js';
|
||||||
import { dbData } from './install.js';
|
import { dbData } from './install.js';
|
||||||
|
|
||||||
export default class Uninstall {
|
export default class Uninstall {
|
||||||
|
@ -19,46 +21,50 @@ export default class Uninstall {
|
||||||
|
|
||||||
public async run(): Promise<void> {
|
public async run(): Promise<void> {
|
||||||
await makeDbFile(this.options.sysroot);
|
await makeDbFile(this.options.sysroot);
|
||||||
|
await checkLockFile(this.options.sysroot);
|
||||||
|
|
||||||
if (this.name) {
|
await lock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg/wahpkg.lock` });
|
||||||
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
|
||||||
const adapter = new lowdb.JSONFile<dbData>(dbFile);
|
|
||||||
const db = new lowdb.Low(adapter);
|
|
||||||
|
|
||||||
await db.read();
|
const dbFile = `${this.options.sysroot}/var/lib/wahpkg/pkgs.json`;
|
||||||
|
const adapter = new JSONFile<dbData>(dbFile);
|
||||||
|
const db = new Low(adapter);
|
||||||
|
|
||||||
if (!db.data.pkgs.find((val) => val.name === this.name)) {
|
await db.read();
|
||||||
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`);
|
if (!db.data.pkgs.find((val) => val.name === this.name)) {
|
||||||
|
console.error(chalk.red.bold`%s is not installed!`, this.name);
|
||||||
for (const file of md5File.toString().split('\n')) {
|
process.exit(1);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
|
||||||
|
await unlock(`${this.options.sysroot}/var/lib/wahpkg`, { lockfilePath: `${this.options.sysroot}/var/lib/wahpkg/wahpkg.lock` });
|
||||||
|
|
||||||
|
process.exit(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
import chalk from 'chalk';
|
import chalk from 'chalk';
|
||||||
import fs from 'fs-extra';
|
import fs from 'fs-extra';
|
||||||
import path from 'node:path';
|
import path from 'node:path';
|
||||||
|
import { check, lock } from 'proper-lockfile';
|
||||||
import type { PartialDeep, RequireAllOrNone } from 'type-fest';
|
import type { PartialDeep, RequireAllOrNone } from 'type-fest';
|
||||||
|
|
||||||
export function parseWahInfo(file: string): WahInfo {
|
export function parseWahInfo(file: string): WahInfo {
|
||||||
|
@ -72,6 +73,10 @@ 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 {
|
export interface WahInfo {
|
||||||
name: string;
|
name: string;
|
||||||
version: {
|
version: {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"compilerOptions": {
|
"compilerOptions": {
|
||||||
"module": "ESNext",
|
"module": "ES2022",
|
||||||
"moduleResolution": "NodeNext",
|
"moduleResolution": "NodeNext",
|
||||||
"lib": [
|
"lib": [
|
||||||
"ES2022",
|
"ES2022",
|
||||||
|
@ -8,6 +8,10 @@
|
||||||
],
|
],
|
||||||
"target": "ES2022",
|
"target": "ES2022",
|
||||||
"allowSyntheticDefaultImports": true,
|
"allowSyntheticDefaultImports": true,
|
||||||
"esModuleInterop": true
|
"esModuleInterop": true,
|
||||||
}
|
"outDir": "dist"
|
||||||
|
},
|
||||||
|
"include": [
|
||||||
|
"src/**/*.ts"
|
||||||
|
]
|
||||||
}
|
}
|
||||||
|
|
Reference in a new issue