Archived
0
0
Fork 0

Compare commits

..

10 commits

31 changed files with 1924 additions and 4590 deletions

1
.envrc Normal file
View file

@ -0,0 +1 @@
use flake

1
.gitignore vendored
View file

@ -9,3 +9,4 @@
node_modules node_modules
dist dist
test test
.direnv

View file

@ -1,4 +0,0 @@
{
"eslint.packageManager": "yarn",
"eslint.nodePath": ".yarn/sdks"
}

View file

@ -1,3 +0,0 @@
{
"typescript.tsdk": ".yarn/sdks/typescript/lib"
}

View file

@ -1,6 +0,0 @@
{
"workspace.workspaceFolderCheckCwd": false,
"tsserver.tsdk": ".yarn/sdks/typescript/lib",
"eslint.packageManager": "yarn",
"eslint.nodePath": ".yarn/sdks"
}

View file

@ -1,6 +0,0 @@
{
"recommendations": [
"arcanis.vscode-zipfs",
"dbaeumer.vscode-eslint"
]
}

View file

@ -1,9 +0,0 @@
{
"search.exclude": {
"**/.yarn": true,
"**/.pnp.*": true
},
"typescript.tsdk": ".yarn/sdks/typescript/lib",
"typescript.enablePromptUseWorkspaceTsdk": true,
"eslint.nodePath": ".yarn/sdks"
}

File diff suppressed because one or more lines are too long

View file

@ -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`);

View file

@ -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`);

View file

@ -1,6 +0,0 @@
{
"name": "eslint",
"version": "8.23.0-sdk",
"main": "./lib/api.js",
"type": "commonjs"
}

View file

@ -1,6 +0,0 @@
# This file is automatically generated by @yarnpkg/sdks.
# Manual changes might be lost!
integrations:
- vscode
- vim

View file

@ -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`);

View file

@ -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`);

View file

@ -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`);

View file

@ -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`));

View file

@ -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`));

View file

@ -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`);

View file

@ -1,6 +0,0 @@
{
"name": "typescript",
"version": "4.8.2-sdk",
"main": "./lib/typescript.js",
"type": "commonjs"
}

View file

@ -1 +0,0 @@
yarnPath: .yarn/releases/yarn-sources.cjs

26
flake.lock Normal file
View 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
View 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;
};
}

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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(),
],
};

View file

@ -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);
} }
} }

View file

@ -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();

View file

@ -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);
} }
} }

View file

@ -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: {

View file

@ -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"
]
} }

3115
yarn.lock

File diff suppressed because it is too large Load diff