mirror of
https://github.com/wavetermdev/inshellisense.git
synced 2026-08-05 13:43:30 -07:00
feat: write initial runtime
Signed-off-by: Chapman Pendery <cpendery@vt.edu>
This commit is contained in:
+16
-3
@@ -1,5 +1,18 @@
|
||||
/** @type {import('ts-jest').JestConfigWithTsJest} */
|
||||
module.exports = {
|
||||
preset: 'ts-jest',
|
||||
testEnvironment: 'node',
|
||||
};
|
||||
// [...]
|
||||
extensionsToTreatAsEsm: [".ts"],
|
||||
moduleNameMapper: {
|
||||
"^(\\.{1,2}/.*)\\.js$": "$1",
|
||||
},
|
||||
transform: {
|
||||
// '^.+\\.[tj]sx?$' to process js/ts with `ts-jest`
|
||||
// '^.+\\.m?[tj]sx?$' to process js/ts/mjs/mts with `ts-jest`
|
||||
"^.+\\.tsx?$": [
|
||||
"ts-jest",
|
||||
{
|
||||
useESM: true,
|
||||
},
|
||||
],
|
||||
},
|
||||
};
|
||||
|
||||
Generated
-288
@@ -16,13 +16,11 @@
|
||||
"wrap-ansi": "^8.1.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@babel/preset-typescript": "^7.23.0",
|
||||
"@tsconfig/node18": "^18.2.2",
|
||||
"@types/ink": "^2.0.3",
|
||||
"@types/jest": "^29.5.5",
|
||||
"@types/react": "^18.2.24",
|
||||
"@withfig/autocomplete-types": "^1.28.0",
|
||||
"cross-env": "^7.0.3",
|
||||
"jest": "^29.7.0",
|
||||
"ts-jest": "^29.1.1",
|
||||
"typescript": "^5.2.2"
|
||||
@@ -185,18 +183,6 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-annotate-as-pure": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
|
||||
"integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-compilation-targets": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
|
||||
@@ -237,38 +223,6 @@
|
||||
"integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz",
|
||||
"integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.22.5",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/helper-member-expression-to-functions": "^7.22.15",
|
||||
"@babel/helper-optimise-call-expression": "^7.22.5",
|
||||
"@babel/helper-replace-supers": "^7.22.9",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-create-class-features-plugin/node_modules/semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true,
|
||||
"bin": {
|
||||
"semver": "bin/semver.js"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-environment-visitor": {
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
@@ -303,18 +257,6 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-member-expression-to-functions": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
|
||||
"integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.23.0"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-module-imports": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
|
||||
@@ -346,18 +288,6 @@
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-optimise-call-expression": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
|
||||
"integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-plugin-utils": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
|
||||
@@ -367,23 +297,6 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-replace-supers": {
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz",
|
||||
"integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-member-expression-to-functions": "^7.22.15",
|
||||
"@babel/helper-optimise-call-expression": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-simple-access": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
|
||||
@@ -396,18 +309,6 @@
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-skip-transparent-expression-wrappers": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
|
||||
"integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/types": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/helper-split-export-declaration": {
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
@@ -720,60 +621,6 @@
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-modules-commonjs": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz",
|
||||
"integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-module-transforms": "^7.23.0",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-simple-access": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/plugin-transform-typescript": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz",
|
||||
"integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-annotate-as-pure": "^7.22.5",
|
||||
"@babel/helper-create-class-features-plugin": "^7.22.15",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-typescript": "^7.22.5"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/preset-typescript": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.0.tgz",
|
||||
"integrity": "sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-validator-option": "^7.22.15",
|
||||
"@babel/plugin-syntax-jsx": "^7.22.5",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.23.0",
|
||||
"@babel/plugin-transform-typescript": "^7.22.15"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=6.9.0"
|
||||
},
|
||||
"peerDependencies": {
|
||||
"@babel/core": "^7.0.0-0"
|
||||
}
|
||||
},
|
||||
"node_modules/@babel/template": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
@@ -2521,24 +2368,6 @@
|
||||
"integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==",
|
||||
"dev": true
|
||||
},
|
||||
"node_modules/cross-env": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
|
||||
"dev": true,
|
||||
"dependencies": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
},
|
||||
"bin": {
|
||||
"cross-env": "src/bin/cross-env.js",
|
||||
"cross-env-shell": "src/bin/cross-env-shell.js"
|
||||
},
|
||||
"engines": {
|
||||
"node": ">=10.14",
|
||||
"npm": ">=6",
|
||||
"yarn": ">=1"
|
||||
}
|
||||
},
|
||||
"node_modules/cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
@@ -5770,15 +5599,6 @@
|
||||
"jsesc": "^2.5.1"
|
||||
}
|
||||
},
|
||||
"@babel/helper-annotate-as-pure": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-annotate-as-pure/-/helper-annotate-as-pure-7.22.5.tgz",
|
||||
"integrity": "sha512-LvBTxu8bQSQkcyKOU+a1btnNFQ1dMAd0R6PyW3arXes06F6QLWLIrd681bxRPIXlrMGR3XYnW9JyML7dP3qgxg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-compilation-targets": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.22.15.tgz",
|
||||
@@ -5815,31 +5635,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-create-class-features-plugin": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-create-class-features-plugin/-/helper-create-class-features-plugin-7.22.15.tgz",
|
||||
"integrity": "sha512-jKkwA59IXcvSaiK2UN45kKwSC9o+KuoXsBDvHvU/7BecYIp8GQ2UwrVvFgJASUT+hBnwJx6MhvMCuMzwZZ7jlg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.22.5",
|
||||
"@babel/helper-environment-visitor": "^7.22.5",
|
||||
"@babel/helper-function-name": "^7.22.5",
|
||||
"@babel/helper-member-expression-to-functions": "^7.22.15",
|
||||
"@babel/helper-optimise-call-expression": "^7.22.5",
|
||||
"@babel/helper-replace-supers": "^7.22.9",
|
||||
"@babel/helper-skip-transparent-expression-wrappers": "^7.22.5",
|
||||
"@babel/helper-split-export-declaration": "^7.22.6",
|
||||
"semver": "^6.3.1"
|
||||
},
|
||||
"dependencies": {
|
||||
"semver": {
|
||||
"version": "6.3.1",
|
||||
"resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz",
|
||||
"integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==",
|
||||
"dev": true
|
||||
}
|
||||
}
|
||||
},
|
||||
"@babel/helper-environment-visitor": {
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-environment-visitor/-/helper-environment-visitor-7.22.20.tgz",
|
||||
@@ -5865,15 +5660,6 @@
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-member-expression-to-functions": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.23.0.tgz",
|
||||
"integrity": "sha512-6gfrPwh7OuT6gZyJZvd6WbTfrqAo7vm4xCzAXOusKqq/vWdKXphTpj5klHKNmRUU6/QRGlBsyU9mAIPaWHlqJA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.23.0"
|
||||
}
|
||||
},
|
||||
"@babel/helper-module-imports": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.22.15.tgz",
|
||||
@@ -5896,32 +5682,12 @@
|
||||
"@babel/helper-validator-identifier": "^7.22.20"
|
||||
}
|
||||
},
|
||||
"@babel/helper-optimise-call-expression": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.22.5.tgz",
|
||||
"integrity": "sha512-HBwaojN0xFRx4yIvpwGqxiV2tUfl7401jlok564NgB9EHS1y6QT17FmKWm4ztqjeVdXLuC4fSvHc5ePpQjoTbw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-plugin-utils": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.22.5.tgz",
|
||||
"integrity": "sha512-uLls06UVKgFG9QD4OeFYLEGteMIAa5kpTPcFL28yuCIIzsf6ZyKZMllKVOCZFhiZ5ptnwX4mtKdWCBE/uT4amg==",
|
||||
"dev": true
|
||||
},
|
||||
"@babel/helper-replace-supers": {
|
||||
"version": "7.22.20",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.22.20.tgz",
|
||||
"integrity": "sha512-qsW0In3dbwQUbK8kejJ4R7IHVGwHJlV6lpG6UA7a9hSa2YEiAib+N1T2kr6PEeUT+Fl7najmSOS6SmAwCHK6Tw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-environment-visitor": "^7.22.20",
|
||||
"@babel/helper-member-expression-to-functions": "^7.22.15",
|
||||
"@babel/helper-optimise-call-expression": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-simple-access": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.22.5.tgz",
|
||||
@@ -5931,15 +5697,6 @@
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-skip-transparent-expression-wrappers": {
|
||||
"version": "7.22.5",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-skip-transparent-expression-wrappers/-/helper-skip-transparent-expression-wrappers-7.22.5.tgz",
|
||||
"integrity": "sha512-tK14r66JZKiC43p8Ki33yLBVJKlQDFoA8GYN67lWCDCqoL6EMMSuM9b+Iff2jHaM/RRFYl7K+iiru7hbRqNx8Q==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/types": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/helper-split-export-declaration": {
|
||||
"version": "7.22.6",
|
||||
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.22.6.tgz",
|
||||
@@ -6164,42 +5921,6 @@
|
||||
"@babel/helper-plugin-utils": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-modules-commonjs": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-modules-commonjs/-/plugin-transform-modules-commonjs-7.23.0.tgz",
|
||||
"integrity": "sha512-32Xzss14/UVc7k9g775yMIvkVK8xwKE0DPdP5JTapr3+Z9w4tzeOuLNY6BXDQR6BdnzIlXnCGAzsk/ICHBLVWQ==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-module-transforms": "^7.23.0",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-simple-access": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/plugin-transform-typescript": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/plugin-transform-typescript/-/plugin-transform-typescript-7.22.15.tgz",
|
||||
"integrity": "sha512-1uirS0TnijxvQLnlv5wQBwOX3E1wCFX7ITv+9pBV2wKEk4K+M5tqDaoNXnTH8tjEIYHLO98MwiTWO04Ggz4XuA==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-annotate-as-pure": "^7.22.5",
|
||||
"@babel/helper-create-class-features-plugin": "^7.22.15",
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/plugin-syntax-typescript": "^7.22.5"
|
||||
}
|
||||
},
|
||||
"@babel/preset-typescript": {
|
||||
"version": "7.23.0",
|
||||
"resolved": "https://registry.npmjs.org/@babel/preset-typescript/-/preset-typescript-7.23.0.tgz",
|
||||
"integrity": "sha512-6P6VVa/NM/VlAYj5s2Aq/gdVg8FSENCg3wlZ6Qau9AcPaoF5LbN1nyGlR9DTRIw9PpxI94e+ReydsJHcjwAweg==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"@babel/helper-plugin-utils": "^7.22.5",
|
||||
"@babel/helper-validator-option": "^7.22.15",
|
||||
"@babel/plugin-syntax-jsx": "^7.22.5",
|
||||
"@babel/plugin-transform-modules-commonjs": "^7.23.0",
|
||||
"@babel/plugin-transform-typescript": "^7.22.15"
|
||||
}
|
||||
},
|
||||
"@babel/template": {
|
||||
"version": "7.22.15",
|
||||
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.22.15.tgz",
|
||||
@@ -7551,15 +7272,6 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"cross-env": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-env/-/cross-env-7.0.3.tgz",
|
||||
"integrity": "sha512-+/HKd6EgcQCJGh2PSjZuUitQBQynKor4wrFbRg4DtAgS1aWO+gU52xpH7M9ScGgXSYmAVS9bIJ8EzuaGw0oNAw==",
|
||||
"dev": true,
|
||||
"requires": {
|
||||
"cross-spawn": "^7.0.1"
|
||||
}
|
||||
},
|
||||
"cross-spawn": {
|
||||
"version": "7.0.3",
|
||||
"resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
|
||||
|
||||
+1
-1
@@ -7,7 +7,7 @@
|
||||
"scripts": {
|
||||
"build": "tsc",
|
||||
"start": "node ./build/index.js",
|
||||
"test": "jest"
|
||||
"test": "node --experimental-vm-modules node_modules/jest/bin/jest.js"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
|
||||
@@ -0,0 +1,47 @@
|
||||
import { exec } from "node:child_process";
|
||||
import { runTemplates } from "./template.js";
|
||||
|
||||
const buildExecuteShellCommand =
|
||||
(timeout: number) =>
|
||||
async (command: string, cwd?: string): Promise<string> => {
|
||||
return new Promise((resolve, reject) => {
|
||||
exec(command, { timeout }, (_, stdout, stderr) => {
|
||||
resolve(stdout || stderr);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
const getGeneratorContext = (): Fig.GeneratorContext => {
|
||||
return {
|
||||
environmentVariables: Object.fromEntries(Object.entries(process.env).filter((entry): entry is [string, string] => entry[1] != null)),
|
||||
currentWorkingDirectory: process.cwd(),
|
||||
currentProcess: "", // TODO: define current process
|
||||
sshPrefix: "", // deprecated, should be empty
|
||||
isDangerous: false,
|
||||
searchTerm: "", // TODO: define search term
|
||||
};
|
||||
};
|
||||
|
||||
// TODO: add support for caching, trigger, & getQueryTerm
|
||||
export const runGenerator = async (generator: Fig.Generator, tokens: string[]): Promise<Fig.Suggestion[]> => {
|
||||
const { script, postProcess, scriptTimeout, splitOn, custom, template } = generator;
|
||||
const executeShellCommand = buildExecuteShellCommand(scriptTimeout ?? 5000);
|
||||
const suggestions = [];
|
||||
if (script) {
|
||||
const scriptOutput = typeof script === "function" ? script(tokens) : script != null ? await executeShellCommand(script) : "";
|
||||
if (postProcess) {
|
||||
suggestions.push(...postProcess(scriptOutput, tokens));
|
||||
} else if (splitOn) {
|
||||
suggestions.push(...scriptOutput.split(splitOn).map((s) => ({ name: s })));
|
||||
}
|
||||
}
|
||||
|
||||
if (custom) {
|
||||
suggestions.push(...(await custom(tokens, executeShellCommand, getGeneratorContext())));
|
||||
}
|
||||
|
||||
if (template != null) {
|
||||
suggestions.push(...runTemplates(template));
|
||||
}
|
||||
return suggestions;
|
||||
};
|
||||
@@ -0,0 +1,5 @@
|
||||
export type Suggestion = {
|
||||
name: string;
|
||||
description?: string;
|
||||
icon: string;
|
||||
};
|
||||
@@ -1,7 +1,8 @@
|
||||
type CommandToken = {
|
||||
export type CommandToken = {
|
||||
token: string;
|
||||
complete: boolean;
|
||||
isOption: boolean;
|
||||
isPersistent?: boolean;
|
||||
};
|
||||
|
||||
const cmdDelim = /(\|\|)|(&&)|(;)/;
|
||||
@@ -9,10 +10,10 @@ const spaceRegex = /\s/;
|
||||
|
||||
export const parseCommand = (command: string): CommandToken[] => {
|
||||
const lastCommand = command.split(cmdDelim).at(-1)?.trimStart();
|
||||
return lastCommand ? parse(lastCommand) : [];
|
||||
return lastCommand ? lex(lastCommand) : [];
|
||||
};
|
||||
|
||||
const parse = (command: string): CommandToken[] => {
|
||||
const lex = (command: string): CommandToken[] => {
|
||||
const tokens: CommandToken[] = [];
|
||||
let [readingQuotedString, readingFlag, readingCmd] = [false, false, false];
|
||||
let readingIdx = 0;
|
||||
|
||||
+200
-21
@@ -2,28 +2,207 @@ import speclist, {
|
||||
diffVersionedCompletions as versionedSpeclist,
|
||||
// @ts-ignore
|
||||
} from "@withfig/autocomplete/build/index.js";
|
||||
import { parseCommand, CommandToken } from "./parser.js";
|
||||
import { SuggestionBlob, getArgDrivenRecommendation, getSubcommandDrivenRecommendation } from "./suggestion.js";
|
||||
|
||||
const specs = (await Promise.all(
|
||||
speclist.map(async (spec: string) => {
|
||||
const prefix = versionedSpeclist.includes(spec) ? "/index.js" : `.js`;
|
||||
return (await import(`@withfig/autocomplete/build/${spec}${prefix}`))
|
||||
.default;
|
||||
})
|
||||
)) as Fig.Spec[];
|
||||
const specSet: any = {};
|
||||
(speclist as string[]).forEach((s) => {
|
||||
let activeSet = specSet;
|
||||
const specRoutes = s.split("/");
|
||||
specRoutes.forEach((route, idx) => {
|
||||
if (idx === specRoutes.length - 1) {
|
||||
const prefix = versionedSpeclist.includes(s) ? "/index.js" : `.js`;
|
||||
activeSet[route] = `@withfig/autocomplete/build/${s}${prefix}`;
|
||||
} else {
|
||||
activeSet[route] = activeSet[route] || {};
|
||||
activeSet = activeSet[route];
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
export const getSuggestions = (cmd: string) => {
|
||||
const suggestions: Fig.Suggestion[] = [
|
||||
{
|
||||
name: "test",
|
||||
description:
|
||||
"test this is a very long description that i'd expect to wrap",
|
||||
},
|
||||
{ name: "test1", description: "test" },
|
||||
{ name: "test2", description: "test" },
|
||||
{ name: "test3", description: "test" },
|
||||
{ name: "test4", description: "test" },
|
||||
{ name: "test5", description: "test" },
|
||||
];
|
||||
const loadedSpecs: { [key: string]: Fig.Spec } = {};
|
||||
|
||||
return suggestions;
|
||||
const loadSpec = async (cmd: CommandToken[]): Promise<Fig.Spec | undefined> => {
|
||||
const rootToken = cmd.at(0);
|
||||
if (!rootToken?.complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (loadedSpecs[rootToken.token]) {
|
||||
return loadedSpecs[rootToken.token];
|
||||
}
|
||||
if (specSet[rootToken.token]) {
|
||||
return (await import(specSet[rootToken.token])).default;
|
||||
}
|
||||
};
|
||||
|
||||
export const getSuggestions = async (cmd: string): Promise<SuggestionBlob | undefined> => {
|
||||
const activeCmd = parseCommand(cmd);
|
||||
const rootToken = activeCmd.at(0);
|
||||
if (activeCmd.length === 0 || !rootToken?.complete) {
|
||||
return;
|
||||
}
|
||||
|
||||
const spec = await loadSpec(activeCmd);
|
||||
if (spec == null) return;
|
||||
const subcommand = getSubcommand(spec);
|
||||
if (subcommand == null) return;
|
||||
|
||||
return runSubcommand(activeCmd.slice(1), subcommand);
|
||||
};
|
||||
|
||||
const getPersistentOptions = (persistentOptions: Fig.Option[], options?: Fig.Option[]) => {
|
||||
const persistentOptionNames = new Set(persistentOptions.map((o) => (typeof o.name === "string" ? [o.name] : o.name)).flat());
|
||||
return persistentOptions.concat(
|
||||
(options ?? []).filter(
|
||||
(o) => (typeof o.name == "string" ? !persistentOptionNames.has(o.name) : o.name.some((n) => !persistentOptionNames.has(n))) && o.isPersistent === true
|
||||
)
|
||||
);
|
||||
};
|
||||
|
||||
// TODO: handle subcommands that are versioned
|
||||
const getSubcommand = (spec: Fig.Spec): Fig.Subcommand | undefined => {
|
||||
if (typeof spec === "function") {
|
||||
const potentialSubcommand = spec();
|
||||
if (potentialSubcommand.hasOwnProperty("name")) {
|
||||
return potentialSubcommand as Fig.Subcommand;
|
||||
}
|
||||
return;
|
||||
}
|
||||
return spec;
|
||||
};
|
||||
|
||||
// TODO: implement loadspec
|
||||
const genSubcommand = (command: string, parentCommand: Fig.Subcommand): Fig.Subcommand | undefined => {
|
||||
switch (typeof parentCommand.loadSpec) {
|
||||
case "function":
|
||||
|
||||
case "string":
|
||||
|
||||
case "object":
|
||||
|
||||
case "undefined":
|
||||
return parentCommand.subcommands?.find((s) => s.name === command);
|
||||
}
|
||||
};
|
||||
|
||||
const getOption = (activeToken: CommandToken, options: Fig.Option[]): Fig.Option | undefined => {
|
||||
return options.find((o) => (typeof o.name === "string" ? o.name === activeToken.token : o.name.includes(activeToken.token)));
|
||||
};
|
||||
|
||||
const getPersistentTokens = (tokens: CommandToken[]): CommandToken[] => {
|
||||
return tokens.filter((t) => t.isPersistent === true);
|
||||
};
|
||||
|
||||
const getArgs = (args: Fig.SingleOrArray<Fig.Arg> | undefined): Fig.Arg[] => {
|
||||
return args instanceof Array ? args : args != null ? [args] : [];
|
||||
};
|
||||
|
||||
const runOption = async (
|
||||
tokens: CommandToken[],
|
||||
option: Fig.Option,
|
||||
subcommand: Fig.Subcommand,
|
||||
persistentOptions: Fig.Option[],
|
||||
acceptedTokens: CommandToken[]
|
||||
): Promise<SuggestionBlob | undefined> => {
|
||||
if (tokens.length === 0) {
|
||||
throw new Error("invalid state reached, option expected but no tokens found");
|
||||
}
|
||||
const activeToken = tokens[0];
|
||||
const isPersistent = persistentOptions.some((o) => (typeof o.name === "string" ? o.name === activeToken.token : o.name.includes(activeToken.token)));
|
||||
if ((option.args instanceof Array && option.args.length > 0) || option.args != null) {
|
||||
const args = option.args instanceof Array ? option.args : [option.args];
|
||||
return runArg(tokens.slice(1), args, subcommand, persistentOptions, acceptedTokens.concat(activeToken), true, false);
|
||||
}
|
||||
return runSubcommand(tokens.slice(1), subcommand, persistentOptions, acceptedTokens.concat({ ...activeToken, isPersistent }));
|
||||
};
|
||||
|
||||
const runArg = async (
|
||||
tokens: CommandToken[],
|
||||
args: Fig.Arg[],
|
||||
subcommand: Fig.Subcommand,
|
||||
persistentOptions: Fig.Option[],
|
||||
acceptedTokens: CommandToken[],
|
||||
fromOption: boolean,
|
||||
fromVariadic: boolean
|
||||
): Promise<SuggestionBlob | undefined> => {
|
||||
if (args.length === 0) {
|
||||
return runSubcommand(tokens, subcommand, persistentOptions, acceptedTokens, true, !fromOption);
|
||||
} else if (tokens.length === 0) {
|
||||
return await getArgDrivenRecommendation(args, subcommand, persistentOptions, undefined, acceptedTokens, fromVariadic);
|
||||
} else if (!tokens.at(0)?.complete) {
|
||||
return await getArgDrivenRecommendation(args, subcommand, persistentOptions, tokens[0].token, acceptedTokens, fromVariadic);
|
||||
}
|
||||
|
||||
const activeToken = tokens[0];
|
||||
if (args.every((a) => a.isOptional)) {
|
||||
if (activeToken.isOption) {
|
||||
const option = getOption(activeToken, persistentOptions.concat(subcommand.options ?? []));
|
||||
if (option != null) {
|
||||
return runOption(tokens, option, subcommand, persistentOptions, acceptedTokens);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const nextSubcommand = genSubcommand(activeToken.token, subcommand);
|
||||
if (nextSubcommand != null) {
|
||||
return runSubcommand(tokens.slice(1), nextSubcommand, persistentOptions, getPersistentTokens(acceptedTokens.concat(activeToken)));
|
||||
}
|
||||
}
|
||||
|
||||
const activeArg = args[0];
|
||||
if (activeArg.isVariadic) {
|
||||
return runArg(tokens.slice(1), args, subcommand, persistentOptions, acceptedTokens.concat(activeToken), fromOption, true);
|
||||
} else if (activeArg.isCommand) {
|
||||
if (tokens.length <= 1) {
|
||||
return;
|
||||
}
|
||||
const activeCmd = tokens[1];
|
||||
// TODO: handle subcommands that isCommands
|
||||
return;
|
||||
}
|
||||
return runArg(tokens.slice(1), args.slice(1), subcommand, persistentOptions, acceptedTokens.concat(activeToken), fromOption, false);
|
||||
};
|
||||
|
||||
const runSubcommand = async (
|
||||
tokens: CommandToken[],
|
||||
subcommand: Fig.Subcommand,
|
||||
persistentOptions: Fig.Option[] = [],
|
||||
acceptedTokens: CommandToken[] = [],
|
||||
argsDepleted = false,
|
||||
argsUsed = false
|
||||
): Promise<SuggestionBlob | undefined> => {
|
||||
if (tokens.length === 0) {
|
||||
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, undefined, argsDepleted, argsUsed, acceptedTokens);
|
||||
} else if (!tokens.at(0)?.complete) {
|
||||
return getSubcommandDrivenRecommendation(subcommand, persistentOptions, tokens[0].token, argsDepleted, argsUsed, acceptedTokens);
|
||||
}
|
||||
|
||||
const activeToken = tokens[0];
|
||||
const activeArgsLength = subcommand.args instanceof Array ? subcommand.args.length : 1;
|
||||
const allOptions = [...persistentOptions, ...(subcommand.options ?? [])];
|
||||
|
||||
if (activeToken.isOption) {
|
||||
const option = getOption(activeToken, allOptions);
|
||||
if (option != null) {
|
||||
return runOption(tokens, option, subcommand, persistentOptions, acceptedTokens);
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
const nextSubcommand = genSubcommand(activeToken.token, subcommand);
|
||||
if (nextSubcommand != null) {
|
||||
return runSubcommand(
|
||||
tokens.slice(1),
|
||||
nextSubcommand,
|
||||
getPersistentOptions(persistentOptions, subcommand.options),
|
||||
getPersistentTokens(acceptedTokens.concat(activeToken))
|
||||
);
|
||||
}
|
||||
|
||||
if (activeArgsLength <= 0) {
|
||||
return; // not subcommand or option & no args exist
|
||||
}
|
||||
|
||||
return runArg(tokens, getArgs(subcommand.args), subcommand, allOptions, acceptedTokens, false, false);
|
||||
};
|
||||
|
||||
@@ -0,0 +1,204 @@
|
||||
import { CommandToken } from "./parser.js";
|
||||
import { runGenerator } from "./generator.js";
|
||||
import { runTemplates } from "./template.js";
|
||||
import { Suggestion } from "./model.js";
|
||||
|
||||
// TODO: support other suggestion attributes
|
||||
enum SuggestionIcons {
|
||||
File = "📄",
|
||||
Folder = "📁",
|
||||
Subcommand = "📦",
|
||||
Option = "⚙️",
|
||||
Argument = "💲",
|
||||
Mixin = "🏝️",
|
||||
Shortcut = "🔥",
|
||||
Special = "⭐",
|
||||
Default = "📀",
|
||||
}
|
||||
|
||||
const getIcon = (suggestionType?: Fig.SuggestionType | undefined): string => {
|
||||
switch (suggestionType) {
|
||||
case "arg":
|
||||
return SuggestionIcons.Argument;
|
||||
case "file":
|
||||
return SuggestionIcons.File;
|
||||
case "folder":
|
||||
return SuggestionIcons.Folder;
|
||||
case "option":
|
||||
return SuggestionIcons.Option;
|
||||
case "subcommand":
|
||||
return SuggestionIcons.Subcommand;
|
||||
case "mixin":
|
||||
return SuggestionIcons.Mixin;
|
||||
case "shortcut":
|
||||
return SuggestionIcons.Shortcut;
|
||||
case "special":
|
||||
return SuggestionIcons.Special;
|
||||
}
|
||||
return SuggestionIcons.Default;
|
||||
};
|
||||
|
||||
const getLong = (suggestion: Fig.SingleOrArray<string>): string => {
|
||||
return suggestion instanceof Array ? suggestion.reduce((p, c) => (p.length > c.length ? p : c)) : suggestion;
|
||||
};
|
||||
|
||||
const toSuggestion = (suggestion: Fig.Suggestion, name?: string, type?: Fig.SuggestionType): Suggestion | undefined => {
|
||||
if (suggestion.name == null) return;
|
||||
return {
|
||||
name: name ?? getLong(suggestion.name),
|
||||
description: suggestion.description,
|
||||
icon: getIcon(type ?? suggestion.type),
|
||||
};
|
||||
};
|
||||
|
||||
function filter<T extends Fig.BaseSuggestion & { name?: Fig.SingleOrArray<string>; type?: Fig.SuggestionType | undefined }>(
|
||||
suggestions: T[],
|
||||
filterStrategy: FilterStrategy | undefined,
|
||||
partialCmd: string | undefined,
|
||||
suggestionType: Fig.SuggestionType | undefined
|
||||
): Suggestion[] {
|
||||
if (!partialCmd) return suggestions.map((s) => toSuggestion(s, undefined, suggestionType)).filter((s) => s != null) as Suggestion[];
|
||||
|
||||
switch (filterStrategy) {
|
||||
case "fuzzy":
|
||||
return suggestions
|
||||
.map((s) => {
|
||||
if (s.name == null) return;
|
||||
if (s.name instanceof Array) {
|
||||
const matchedName = s.name.find((n) => n.toLowerCase().includes(partialCmd.toLowerCase()));
|
||||
return matchedName != null ? { name: matchedName, description: s.description, icon: getIcon(s.type ?? suggestionType) } : undefined;
|
||||
}
|
||||
return s.name.toLowerCase().includes(partialCmd.toLowerCase())
|
||||
? { name: s.name, description: s.description, icon: getIcon(s.type ?? suggestionType) }
|
||||
: undefined;
|
||||
})
|
||||
.filter((s) => s != null) as Suggestion[];
|
||||
default:
|
||||
return suggestions
|
||||
.map((s) => {
|
||||
if (s.name == null) return;
|
||||
if (s.name instanceof Array) {
|
||||
const matchedName = s.name.find((n) => n.toLowerCase().startsWith(partialCmd.toLowerCase()));
|
||||
return matchedName != null ? { name: matchedName, description: s.description, icon: getIcon(s.type ?? suggestionType) } : undefined;
|
||||
}
|
||||
return s.name.toLowerCase().startsWith(partialCmd.toLowerCase())
|
||||
? { name: s.name, description: s.description, icon: getIcon(s.type ?? suggestionType) }
|
||||
: undefined;
|
||||
})
|
||||
.filter((s) => s != null) as Suggestion[];
|
||||
}
|
||||
}
|
||||
|
||||
export type SuggestionBlob = {
|
||||
suggestions: Suggestion[];
|
||||
argumentDescription?: string;
|
||||
};
|
||||
|
||||
type FilterStrategy = "fuzzy" | "prefix" | "default";
|
||||
|
||||
const generatorSuggestions = async (
|
||||
generator: Fig.SingleOrArray<Fig.Generator> | undefined,
|
||||
acceptedTokens: CommandToken[],
|
||||
filterStrategy: FilterStrategy | undefined,
|
||||
partialCmd: string | undefined
|
||||
): Promise<Suggestion[]> => {
|
||||
const generators = generator instanceof Array ? generator : generator ? [generator] : [];
|
||||
const tokens = acceptedTokens.map((t) => t.token);
|
||||
const suggestions = (await Promise.all(generators.map((gen) => runGenerator(gen, tokens)))).flat();
|
||||
return filter<Fig.Suggestion>(suggestions, filterStrategy, partialCmd, undefined);
|
||||
};
|
||||
|
||||
const templateSuggestions = (templates: Fig.Template | undefined, filterStrategy: FilterStrategy | undefined, partialCmd: string | undefined): Suggestion[] => {
|
||||
return filter<Fig.Suggestion>(runTemplates(templates ?? []), filterStrategy, partialCmd, undefined);
|
||||
};
|
||||
|
||||
const suggestionSuggestions = (
|
||||
suggestions: (string | Fig.Suggestion)[] | undefined,
|
||||
filterStrategy: FilterStrategy | undefined,
|
||||
partialCmd: string | undefined
|
||||
): Suggestion[] => {
|
||||
const cleanedSuggestions = suggestions?.map((s) => (typeof s === "string" ? { name: s } : s)) ?? [];
|
||||
return filter<Fig.Suggestion>(cleanedSuggestions ?? [], filterStrategy, partialCmd, undefined);
|
||||
};
|
||||
|
||||
const subcommandSuggestions = (
|
||||
subcommands: Fig.Subcommand[] | undefined,
|
||||
filterStrategy: FilterStrategy | undefined,
|
||||
partialCmd: string | undefined
|
||||
): Suggestion[] => {
|
||||
return filter<Fig.Subcommand>(subcommands ?? [], filterStrategy, partialCmd, "subcommand");
|
||||
};
|
||||
|
||||
const optionSuggestions = (
|
||||
options: Fig.Option[] | undefined,
|
||||
acceptedTokens: CommandToken[],
|
||||
filterStrategy: FilterStrategy | undefined,
|
||||
partialCmd: string | undefined
|
||||
): Suggestion[] => {
|
||||
const usedOptions = new Set(acceptedTokens.filter((t) => t.isOption).map((t) => t.token));
|
||||
const validOptions = options?.filter((o) => o.exclusiveOn?.every((exclusiveOption) => !usedOptions.has(exclusiveOption)) ?? true);
|
||||
return filter<Fig.Option>(validOptions ?? [], filterStrategy, partialCmd, "option");
|
||||
};
|
||||
|
||||
// TODO: handle case where short option has been seen, but now we recommend long option (see: completedOptionWithArg --actor flag)
|
||||
const removeDuplicateSuggestions = (suggestions: Suggestion[], acceptedTokens: CommandToken[]): Suggestion[] => {
|
||||
const seen = new Set<string>(acceptedTokens.map((t) => t.token));
|
||||
return suggestions.filter((s) => !seen.has(s.name));
|
||||
};
|
||||
|
||||
// TODO: implement re-ranking globally
|
||||
export const getSubcommandDrivenRecommendation = async (
|
||||
subcommand: Fig.Subcommand,
|
||||
persistentOptions: Fig.Option[],
|
||||
partialCmd: string | undefined,
|
||||
argsDepleted: boolean,
|
||||
argsFromSubcommand: boolean,
|
||||
acceptedTokens: CommandToken[]
|
||||
): Promise<SuggestionBlob | undefined> => {
|
||||
if (argsDepleted && argsFromSubcommand) {
|
||||
return;
|
||||
}
|
||||
const suggestions: Suggestion[] = [];
|
||||
const argLength = subcommand.args instanceof Array ? subcommand.args.length : subcommand.args ? 1 : 0;
|
||||
const allOptions = persistentOptions.concat(subcommand.options ?? []);
|
||||
if (argLength != 0) {
|
||||
const activeArg = subcommand.args instanceof Array ? subcommand.args[0] : subcommand.args;
|
||||
suggestions.push(...(await generatorSuggestions(activeArg?.generators, acceptedTokens, activeArg?.filterStrategy, partialCmd)));
|
||||
suggestions.push(...suggestionSuggestions(activeArg?.suggestions, activeArg?.filterStrategy, partialCmd));
|
||||
suggestions.push(...templateSuggestions(activeArg?.template, activeArg?.filterStrategy, partialCmd));
|
||||
}
|
||||
if (!argsFromSubcommand) {
|
||||
suggestions.push(...optionSuggestions(allOptions, acceptedTokens, subcommand.filterStrategy, partialCmd));
|
||||
suggestions.push(...subcommandSuggestions(subcommand.subcommands, subcommand.filterStrategy, partialCmd));
|
||||
}
|
||||
return {
|
||||
suggestions: removeDuplicateSuggestions(suggestions, acceptedTokens),
|
||||
};
|
||||
};
|
||||
|
||||
export const getArgDrivenRecommendation = async (
|
||||
args: Fig.Arg[],
|
||||
subcommand: Fig.Subcommand,
|
||||
persistentOptions: Fig.Option[],
|
||||
partialCmd: string | undefined,
|
||||
acceptedTokens: CommandToken[],
|
||||
variadicArgBound: boolean
|
||||
): Promise<SuggestionBlob | undefined> => {
|
||||
const activeArg = args[0];
|
||||
const allOptions = persistentOptions.concat(subcommand.options ?? []);
|
||||
const suggestions = [
|
||||
...(await generatorSuggestions(args[0].generators, acceptedTokens, activeArg?.filterStrategy, partialCmd)),
|
||||
...suggestionSuggestions(args[0].suggestions, activeArg?.filterStrategy, partialCmd),
|
||||
...templateSuggestions(args[0].template, activeArg?.filterStrategy, partialCmd),
|
||||
];
|
||||
|
||||
if ((activeArg.isOptional && !activeArg.isVariadic) || (activeArg.isVariadic && activeArg.isOptional && !variadicArgBound)) {
|
||||
suggestions.push(...subcommandSuggestions(subcommand.subcommands, activeArg?.filterStrategy, partialCmd));
|
||||
suggestions.push(...optionSuggestions(allOptions, acceptedTokens, activeArg?.filterStrategy, partialCmd));
|
||||
}
|
||||
|
||||
return {
|
||||
suggestions: removeDuplicateSuggestions(suggestions, acceptedTokens),
|
||||
argumentDescription: activeArg.description ?? activeArg.name,
|
||||
};
|
||||
};
|
||||
@@ -0,0 +1,33 @@
|
||||
const filepathsTemplate = (): Fig.Suggestion[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const foldersTemplate = (): Fig.Suggestion[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const historyTemplate = (): Fig.Suggestion[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
const helpTemplate = (): Fig.Suggestion[] => {
|
||||
return [];
|
||||
};
|
||||
|
||||
export const runTemplates = (template: Fig.TemplateStrings[] | Fig.Template): Fig.Suggestion[] => {
|
||||
const templates = template instanceof Array ? template : [template];
|
||||
return templates
|
||||
.map((t) => {
|
||||
switch (t) {
|
||||
case "filepaths":
|
||||
return filepathsTemplate();
|
||||
case "folders":
|
||||
return foldersTemplate();
|
||||
case "history":
|
||||
return historyTemplate();
|
||||
case "help":
|
||||
return helpTemplate();
|
||||
}
|
||||
})
|
||||
.flat();
|
||||
};
|
||||
@@ -0,0 +1,258 @@
|
||||
// Jest Snapshot v1, https://goo.gl/fbAQLP
|
||||
|
||||
exports[`parseCommand alreadyUsedOption 1`] = `
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"description": "Display system information for bug report",
|
||||
"icon": "⚙️",
|
||||
"name": "--bug-report",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand alreadyUsedSuggestion 1`] = `undefined`;
|
||||
|
||||
exports[`parseCommand completePrefixFilter 1`] = `
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"description": "Show the working tree status",
|
||||
"icon": "📦",
|
||||
"name": "status",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand completedOptionWithArg 1`] = `
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"description": "User that triggered the event",
|
||||
"icon": "⚙️",
|
||||
"name": "--actor",
|
||||
},
|
||||
{
|
||||
"description": "Defines the path where the artifact server stores uploads and retrieves downloads from. If not specified the artifact server will not start",
|
||||
"icon": "⚙️",
|
||||
"name": "--artifact-server-path",
|
||||
},
|
||||
{
|
||||
"description": "Defines the port where the artifact server listens (will only bind to localhost)",
|
||||
"icon": "⚙️",
|
||||
"name": "--artifact-server-port",
|
||||
},
|
||||
{
|
||||
"description": "Architecture which should be used to run containers, e.g.: linux/amd64. If not specified, will use host default architecture. Requires Docker server API Version 1.41+. Ignored on earlier Docker server platforms",
|
||||
"icon": "⚙️",
|
||||
"name": "--container-architecture",
|
||||
},
|
||||
{
|
||||
"description": "Path to Docker daemon socket which will be mounted to containers",
|
||||
"icon": "⚙️",
|
||||
"name": "--container-daemon-socket",
|
||||
},
|
||||
{
|
||||
"description": "Working directory",
|
||||
"icon": "⚙️",
|
||||
"name": "--directory",
|
||||
},
|
||||
{
|
||||
"description": "Dryrun mode",
|
||||
"icon": "⚙️",
|
||||
"name": "--dryrun",
|
||||
},
|
||||
{
|
||||
"description": "Environment file to read and use as env in the containers",
|
||||
"icon": "⚙️",
|
||||
"name": "--env-file",
|
||||
},
|
||||
{
|
||||
"description": "GitHub instance to use. Don't use this if you are not using GitHub Enterprise Server",
|
||||
"icon": "⚙️",
|
||||
"name": "--github-instance",
|
||||
},
|
||||
{
|
||||
"description": "NOT RECOMMENDED! Doesn't hide secrets while printing logs",
|
||||
"icon": "⚙️",
|
||||
"name": "--insecure-secrets",
|
||||
},
|
||||
{
|
||||
"description": "Output logs in json format",
|
||||
"icon": "⚙️",
|
||||
"name": "--json",
|
||||
},
|
||||
{
|
||||
"description": "Flag to disable running workflows from subdirectories of specified path in '--workflows'/'-W' flag",
|
||||
"icon": "⚙️",
|
||||
"name": "--no-recurse",
|
||||
},
|
||||
{
|
||||
"description": "Do not skip actions/checkout",
|
||||
"icon": "⚙️",
|
||||
"name": "--no-skip-checkout",
|
||||
},
|
||||
{
|
||||
"description": "Disable logging of output from steps",
|
||||
"icon": "⚙️",
|
||||
"name": "--quiet",
|
||||
},
|
||||
{
|
||||
"description": "File with list of secrets to read from (e.g. --secret-file .secrets)",
|
||||
"icon": "⚙️",
|
||||
"name": "--secret-file",
|
||||
},
|
||||
{
|
||||
"description": "Verbose output",
|
||||
"icon": "⚙️",
|
||||
"name": "--verbose",
|
||||
},
|
||||
{
|
||||
"description": "Path to workflow file(s)",
|
||||
"icon": "⚙️",
|
||||
"name": "--workflows",
|
||||
},
|
||||
{
|
||||
"description": "Display help",
|
||||
"icon": "⚙️",
|
||||
"name": "--help",
|
||||
},
|
||||
{
|
||||
"description": "Disable completion descriptions",
|
||||
"icon": "⚙️",
|
||||
"name": "--no-descriptions",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand emptySuggestions 1`] = `
|
||||
{
|
||||
"suggestions": [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand exclusiveOnOption 1`] = `
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"description": "Print a newline between matches in different files. Enabled by default",
|
||||
"icon": "⚙️",
|
||||
"name": "--nobreak",
|
||||
},
|
||||
{
|
||||
"description": "Don't print color codes in results",
|
||||
"icon": "⚙️",
|
||||
"name": "--nocolor",
|
||||
},
|
||||
{
|
||||
"description": "Don't print file names",
|
||||
"icon": "⚙️",
|
||||
"name": "--nofilename",
|
||||
},
|
||||
{
|
||||
"description": "Don't follow symlinks",
|
||||
"icon": "⚙️",
|
||||
"name": "--nofollow",
|
||||
},
|
||||
{
|
||||
"description": "Refrains from lumping matches in the same file together, and instead places the filename at the start of each match line",
|
||||
"icon": "⚙️",
|
||||
"name": "--nogroup",
|
||||
},
|
||||
{
|
||||
"description": "Don't print filenames above matching contents",
|
||||
"icon": "⚙️",
|
||||
"name": "--noheading",
|
||||
},
|
||||
{
|
||||
"description": "Don't use of memory-mapped I/O. Defaults to true on platforms where mmap() is faster than read(). (All but macOS.)",
|
||||
"icon": "⚙️",
|
||||
"name": "--nommap",
|
||||
},
|
||||
{
|
||||
"description": "Don't match regexes across newlines",
|
||||
"icon": "⚙️",
|
||||
"name": "--nomultiline",
|
||||
},
|
||||
{
|
||||
"description": "Don't recurse into directories",
|
||||
"icon": "⚙️",
|
||||
"name": "--norecurse",
|
||||
},
|
||||
{
|
||||
"description": "Don't print line numbers",
|
||||
"icon": "⚙️",
|
||||
"name": "--nonumbers",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand fullyTypedSuggestion 1`] = `
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"description": "Display whiteouts when scanning directories. (-S) flag)",
|
||||
"icon": "⚙️",
|
||||
"name": "-W",
|
||||
},
|
||||
{
|
||||
"description": "Force raw printing of non-printable characters. This is the default when output is not to a terminal",
|
||||
"icon": "⚙️",
|
||||
"name": "-w",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand noOptionsSuggestedAfterVariadicArg 1`] = `
|
||||
{
|
||||
"argumentDescription": undefined,
|
||||
"suggestions": [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand partialPrefixFilter 1`] = `
|
||||
{
|
||||
"suggestions": [
|
||||
{
|
||||
"description": "Add file contents to the staging area",
|
||||
"icon": "📦",
|
||||
"name": "stage",
|
||||
},
|
||||
{
|
||||
"description": "Show the working tree status",
|
||||
"icon": "📦",
|
||||
"name": "status",
|
||||
},
|
||||
{
|
||||
"description": "Temporarily stores all the modified tracked files",
|
||||
"icon": "📦",
|
||||
"name": "stash",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand providedArgDescription 1`] = `
|
||||
{
|
||||
"argumentDescription": "actor",
|
||||
"suggestions": [],
|
||||
}
|
||||
`;
|
||||
|
||||
exports[`parseCommand providedSuggestion 1`] = `
|
||||
{
|
||||
"argumentDescription": "Shell to generate completions for",
|
||||
"suggestions": [
|
||||
{
|
||||
"description": undefined,
|
||||
"icon": "📀",
|
||||
"name": "zsh",
|
||||
},
|
||||
],
|
||||
}
|
||||
`;
|
||||
@@ -1,4 +1,4 @@
|
||||
import { parseCommand } from "../../runtime/parser";
|
||||
import { parseCommand } from "../../runtime/parser.js";
|
||||
|
||||
const testData = [
|
||||
{ command: `cmd --flag value` },
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
import { getSuggestions } from "../../runtime/runtime.js";
|
||||
|
||||
const testData = [
|
||||
{ name: "partialPrefixFilter", command: "git sta" },
|
||||
{ name: "completePrefixFilter", command: "git stat" },
|
||||
{ name: "emptySuggestions", command: "git to" },
|
||||
{ name: "alreadyUsedSuggestion", command: "cd ~ " },
|
||||
{ name: "alreadyUsedOption", command: "act --bind --b" },
|
||||
{ name: "exclusiveOnOption", command: "ag --affinity --no" },
|
||||
{ name: "providedSuggestion", command: "bw completion --shell " },
|
||||
{ name: "fullyTypedSuggestion", command: "ls -W" },
|
||||
{ name: "noOptionsSuggestedAfterVariadicArg", command: "ls item -" },
|
||||
{ name: "providedArgDescription", command: "act completion bash -a " },
|
||||
{ name: "completedOptionWithArg", command: "act completion bash -a 'actor' " },
|
||||
{ name: "command", command: "sudo git sta", skip: true }, // TODO: fix skipped test
|
||||
{ name: "nestedNonCommands", command: "az az ", skip: true }, // TODO: fix skipped test
|
||||
];
|
||||
|
||||
describe(`parseCommand`, () => {
|
||||
testData.forEach(({ command, name, skip }) => {
|
||||
if (skip) return;
|
||||
test(name, async () => {
|
||||
const suggetions = await getSuggestions(command);
|
||||
expect(suggetions).toMatchSnapshot();
|
||||
});
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user