@neurodevs/ndx-cli 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (47) hide show
  1. package/.nvmrc +1 -0
  2. package/.vscode/launch.json +58 -0
  3. package/.vscode/settings.json +67 -0
  4. package/.vscode/tasks.json +112 -0
  5. package/LICENSE +21 -0
  6. package/README.md +2 -0
  7. package/build/.spruce/settings.json +11 -0
  8. package/build/__tests__/modules/CliCommandRunner.test.d.ts +18 -0
  9. package/build/__tests__/modules/CliCommandRunner.test.js +132 -0
  10. package/build/__tests__/modules/CliCommandRunner.test.js.map +1 -0
  11. package/build/__tests__/modules/NeurodevsAutocloner.test.d.ts +14 -0
  12. package/build/__tests__/modules/NeurodevsAutocloner.test.js +122 -0
  13. package/build/__tests__/modules/NeurodevsAutocloner.test.js.map +1 -0
  14. package/build/cli.d.ts +2 -0
  15. package/build/cli.js +17 -0
  16. package/build/cli.js.map +1 -0
  17. package/build/index.d.ts +0 -0
  18. package/build/index.js +3 -0
  19. package/build/index.js.map +1 -0
  20. package/build/modules/CliCommandRunner.d.ts +23 -0
  21. package/build/modules/CliCommandRunner.js +64 -0
  22. package/build/modules/CliCommandRunner.js.map +1 -0
  23. package/build/modules/NeurodevsAutocloner.d.ts +18 -0
  24. package/build/modules/NeurodevsAutocloner.js +60 -0
  25. package/build/modules/NeurodevsAutocloner.js.map +1 -0
  26. package/build/scripts/runNeurodevsAutocloner.d.ts +1 -0
  27. package/build/scripts/runNeurodevsAutocloner.js +15 -0
  28. package/build/scripts/runNeurodevsAutocloner.js.map +1 -0
  29. package/build/testDoubles/PresetUrlsAutocloner/FakeNeurodevsAutocloner.d.ts +7 -0
  30. package/build/testDoubles/PresetUrlsAutocloner/FakeNeurodevsAutocloner.js +15 -0
  31. package/build/testDoubles/PresetUrlsAutocloner/FakeNeurodevsAutocloner.js.map +1 -0
  32. package/build/testDoubles/prompts/fakePrompts.d.ts +5 -0
  33. package/build/testDoubles/prompts/fakePrompts.js +19 -0
  34. package/build/testDoubles/prompts/fakePrompts.js.map +1 -0
  35. package/eslint.config.mjs +3 -0
  36. package/package.json +71 -0
  37. package/src/.spruce/settings.json +11 -0
  38. package/src/__tests__/modules/CliCommandRunner.test.ts +121 -0
  39. package/src/__tests__/modules/NeurodevsAutocloner.test.ts +94 -0
  40. package/src/cli.ts +15 -0
  41. package/src/index.ts +1 -0
  42. package/src/modules/CliCommandRunner.ts +86 -0
  43. package/src/modules/NeurodevsAutocloner.ts +77 -0
  44. package/src/scripts/runNeurodevsAutocloner.ts +11 -0
  45. package/src/testDoubles/PresetUrlsAutocloner/FakeNeurodevsAutocloner.ts +15 -0
  46. package/src/testDoubles/prompts/fakePrompts.ts +16 -0
  47. package/tsconfig.json +39 -0
@@ -0,0 +1,64 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const meta_node_1 = require("@neurodevs/meta-node");
7
+ const prompts_1 = __importDefault(require("prompts"));
8
+ class CliCommandRunner {
9
+ constructor(args) {
10
+ this.interfaceNameMessage = 'What should the interface be called? Example: YourClass';
11
+ this.implNameMessage = 'What should the implementation class be called? Example: YourClassImpl';
12
+ this.args = args;
13
+ }
14
+ static Create(args) {
15
+ return new (this.Class ?? this)(args);
16
+ }
17
+ async run() {
18
+ this.throwIfCommandIsNotSupported();
19
+ const { interfaceName, implName } = await this.promptUserInput();
20
+ this.currentInterfaceName = interfaceName;
21
+ this.currentImplName = implName;
22
+ await this.createModule();
23
+ }
24
+ throwIfCommandIsNotSupported() {
25
+ if (this.command !== 'create.module') {
26
+ throw new Error(`The command "${this.command}" is not supported!`);
27
+ }
28
+ }
29
+ get command() {
30
+ return this.args[0];
31
+ }
32
+ async promptUserInput() {
33
+ return await this.prompts([
34
+ {
35
+ type: 'text',
36
+ name: 'interfaceName',
37
+ message: this.interfaceNameMessage,
38
+ },
39
+ {
40
+ type: 'text',
41
+ name: 'implName',
42
+ message: this.implNameMessage,
43
+ },
44
+ ]);
45
+ }
46
+ get prompts() {
47
+ return CliCommandRunner.prompts;
48
+ }
49
+ async createModule() {
50
+ const automodule = this.NodeAutomodule();
51
+ await automodule.run();
52
+ }
53
+ NodeAutomodule() {
54
+ return meta_node_1.NodeAutomodule.Create({
55
+ testSaveDir: 'src/__tests__/modules',
56
+ moduleSaveDir: 'src/modules',
57
+ interfaceName: this.currentInterfaceName,
58
+ implName: this.currentImplName,
59
+ });
60
+ }
61
+ }
62
+ CliCommandRunner.prompts = prompts_1.default;
63
+ exports.default = CliCommandRunner;
64
+ //# sourceMappingURL=CliCommandRunner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"CliCommandRunner.js","sourceRoot":"","sources":["../../src/modules/CliCommandRunner.ts"],"names":[],"mappings":";;;;;AAAA,oDAAqD;AACrD,sDAA6B;AAE7B,MAAqB,gBAAgB;IASjC,YAAsB,IAAc;QA4CnB,yBAAoB,GACjC,yDAAyD,CAAA;QAE5C,oBAAe,GAC5B,wEAAwE,CAAA;QA/CxE,IAAI,CAAC,IAAI,GAAG,IAAI,CAAA;IACpB,CAAC;IAEM,MAAM,CAAC,MAAM,CAAC,IAAc;QAC/B,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,IAAI,CAAC,CAAA;IACzC,CAAC;IAEM,KAAK,CAAC,GAAG;QACZ,IAAI,CAAC,4BAA4B,EAAE,CAAA;QAEnC,MAAM,EAAE,aAAa,EAAE,QAAQ,EAAE,GAAG,MAAM,IAAI,CAAC,eAAe,EAAE,CAAA;QAEhE,IAAI,CAAC,oBAAoB,GAAG,aAAa,CAAA;QACzC,IAAI,CAAC,eAAe,GAAG,QAAQ,CAAA;QAE/B,MAAM,IAAI,CAAC,YAAY,EAAE,CAAA;IAC7B,CAAC;IAEO,4BAA4B;QAChC,IAAI,IAAI,CAAC,OAAO,KAAK,eAAe,EAAE,CAAC;YACnC,MAAM,IAAI,KAAK,CAAC,gBAAgB,IAAI,CAAC,OAAO,qBAAqB,CAAC,CAAA;QACtE,CAAC;IACL,CAAC;IAED,IAAY,OAAO;QACf,OAAO,IAAI,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IACvB,CAAC;IAEO,KAAK,CAAC,eAAe;QACzB,OAAO,MAAM,IAAI,CAAC,OAAO,CAAC;YACtB;gBACI,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,eAAe;gBACrB,OAAO,EAAE,IAAI,CAAC,oBAAoB;aACrC;YACD;gBACI,IAAI,EAAE,MAAM;gBACZ,IAAI,EAAE,UAAU;gBAChB,OAAO,EAAE,IAAI,CAAC,eAAe;aAChC;SACJ,CAAC,CAAA;IACN,CAAC;IAQD,IAAY,OAAO;QACf,OAAO,gBAAgB,CAAC,OAAO,CAAA;IACnC,CAAC;IAEO,KAAK,CAAC,YAAY;QACtB,MAAM,UAAU,GAAG,IAAI,CAAC,cAAc,EAAE,CAAA;QACxC,MAAM,UAAU,CAAC,GAAG,EAAE,CAAA;IAC1B,CAAC;IAEO,cAAc;QAClB,OAAO,0BAAc,CAAC,MAAM,CAAC;YACzB,WAAW,EAAE,uBAAuB;YACpC,aAAa,EAAE,aAAa;YAC5B,aAAa,EAAE,IAAI,CAAC,oBAAoB;YACxC,QAAQ,EAAE,IAAI,CAAC,eAAe;SACjC,CAAC,CAAA;IACN,CAAC;;AAzEa,wBAAO,GAAG,iBAAO,AAAV,CAAU;kBAFd,gBAAgB"}
@@ -0,0 +1,18 @@
1
+ import { Autocloner } from '@neurodevs/meta-node';
2
+ export default class NeurodevsAutocloner implements PresetUrlsAutocloner {
3
+ static Class?: PresetUrlsAutoclonerConstructor;
4
+ private autocloner;
5
+ private dirPath;
6
+ protected constructor(autocloner: Autocloner);
7
+ static Create(): PresetUrlsAutocloner;
8
+ run(dirPath: string): Promise<void>;
9
+ private runGitCloner;
10
+ private repoNames;
11
+ private repoUrls;
12
+ private generateUrl;
13
+ private static GitAutocloner;
14
+ }
15
+ export interface PresetUrlsAutocloner {
16
+ run(dirPath: string): Promise<void>;
17
+ }
18
+ export type PresetUrlsAutoclonerConstructor = new (cloner: Autocloner) => PresetUrlsAutocloner;
@@ -0,0 +1,60 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const meta_node_1 = require("@neurodevs/meta-node");
4
+ class NeurodevsAutocloner {
5
+ constructor(autocloner) {
6
+ this.repoNames = [
7
+ 'fili.js',
8
+ 'labrecorder',
9
+ 'liblsl',
10
+ 'libxdf',
11
+ 'node-autocloner',
12
+ 'node-autopackage',
13
+ 'node-autoupgrader',
14
+ 'node-biometrics',
15
+ 'node-biosensors',
16
+ 'node-biosignal-experiments',
17
+ 'node-ble',
18
+ 'node-csv',
19
+ 'node-eeg',
20
+ 'node-file-checker',
21
+ 'node-file-loader',
22
+ 'node-html-loader',
23
+ 'node-knowledge-graphs',
24
+ 'node-lsl',
25
+ 'node-mangled-names',
26
+ 'node-neuropype',
27
+ 'node-ppg',
28
+ 'node-server-plots',
29
+ 'node-signal-processing',
30
+ 'node-task-queue',
31
+ 'node-test-counter',
32
+ 'node-xdf',
33
+ 'personomic',
34
+ ];
35
+ this.repoUrls = this.repoNames.map(this.generateUrl);
36
+ this.autocloner = autocloner;
37
+ }
38
+ static Create() {
39
+ const autocloner = this.GitAutocloner();
40
+ return new (this.Class ?? this)(autocloner);
41
+ }
42
+ async run(dirPath) {
43
+ this.dirPath = dirPath;
44
+ await this.runGitCloner();
45
+ }
46
+ async runGitCloner() {
47
+ await this.autocloner.run({
48
+ urls: this.repoUrls,
49
+ dirPath: this.dirPath,
50
+ });
51
+ }
52
+ generateUrl(repoName) {
53
+ return `https://github.com/neurodevs/${repoName}.git`;
54
+ }
55
+ static GitAutocloner() {
56
+ return meta_node_1.GitAutocloner.Create();
57
+ }
58
+ }
59
+ exports.default = NeurodevsAutocloner;
60
+ //# sourceMappingURL=NeurodevsAutocloner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"NeurodevsAutocloner.js","sourceRoot":"","sources":["../../src/modules/NeurodevsAutocloner.ts"],"names":[],"mappings":";;AAAA,oDAAgE;AAEhE,MAAqB,mBAAmB;IAMpC,YAAsB,UAAsB;QAqBpC,cAAS,GAAG;YAChB,SAAS;YACT,aAAa;YACb,QAAQ;YACR,QAAQ;YACR,iBAAiB;YACjB,kBAAkB;YAClB,mBAAmB;YACnB,iBAAiB;YACjB,iBAAiB;YACjB,4BAA4B;YAC5B,UAAU;YACV,UAAU;YACV,UAAU;YACV,mBAAmB;YACnB,kBAAkB;YAClB,kBAAkB;YAClB,uBAAuB;YACvB,UAAU;YACV,oBAAoB;YACpB,gBAAgB;YAChB,UAAU;YACV,mBAAmB;YACnB,wBAAwB;YACxB,iBAAiB;YACjB,mBAAmB;YACnB,UAAU;YACV,YAAY;SACf,CAAA;QAEO,aAAQ,GAAG,IAAI,CAAC,SAAS,CAAC,GAAG,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QAlDnD,IAAI,CAAC,UAAU,GAAG,UAAU,CAAA;IAChC,CAAC;IAEM,MAAM,CAAC,MAAM;QAChB,MAAM,UAAU,GAAG,IAAI,CAAC,aAAa,EAAE,CAAA;QACvC,OAAO,IAAI,CAAC,IAAI,CAAC,KAAK,IAAI,IAAI,CAAC,CAAC,UAAU,CAAC,CAAA;IAC/C,CAAC;IAEM,KAAK,CAAC,GAAG,CAAC,OAAe;QAC5B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAA;QACtB,MAAM,IAAI,CAAC,YAAY,EAAE,CAAA;IAC7B,CAAC;IAEO,KAAK,CAAC,YAAY;QACtB,MAAM,IAAI,CAAC,UAAU,CAAC,GAAG,CAAC;YACtB,IAAI,EAAE,IAAI,CAAC,QAAQ;YACnB,OAAO,EAAE,IAAI,CAAC,OAAO;SACxB,CAAC,CAAA;IACN,CAAC;IAkCO,WAAW,CAAC,QAAgB;QAChC,OAAO,gCAAgC,QAAQ,MAAM,CAAA;IACzD,CAAC;IAEO,MAAM,CAAC,aAAa;QACxB,OAAO,yBAAa,CAAC,MAAM,EAAE,CAAA;IACjC,CAAC;CACJ;AAlED,sCAkEC"}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ var __importDefault = (this && this.__importDefault) || function (mod) {
3
+ return (mod && mod.__esModule) ? mod : { "default": mod };
4
+ };
5
+ Object.defineProperty(exports, "__esModule", { value: true });
6
+ const NeurodevsAutocloner_1 = __importDefault(require("../modules/NeurodevsAutocloner"));
7
+ async function main() {
8
+ const cloner = NeurodevsAutocloner_1.default.Create();
9
+ await cloner.run('/Users/ericthecurious/dev');
10
+ }
11
+ main().catch((err) => {
12
+ console.error(err);
13
+ process.exit(1);
14
+ });
15
+ //# sourceMappingURL=runNeurodevsAutocloner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"runNeurodevsAutocloner.js","sourceRoot":"","sources":["../../src/scripts/runNeurodevsAutocloner.ts"],"names":[],"mappings":";;;;;AAAA,yFAAgE;AAEhE,KAAK,UAAU,IAAI;IACf,MAAM,MAAM,GAAG,6BAAmB,CAAC,MAAM,EAAE,CAAA;IAC3C,MAAM,MAAM,CAAC,GAAG,CAAC,2BAA2B,CAAC,CAAA;AACjD,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACjB,OAAO,CAAC,KAAK,CAAC,GAAG,CAAC,CAAA;IAClB,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;AACnB,CAAC,CAAC,CAAA"}
@@ -0,0 +1,7 @@
1
+ import { PresetUrlsAutocloner } from '../../modules/NeurodevsAutocloner';
2
+ export default class FakeNeurodevsAutocloner implements PresetUrlsAutocloner {
3
+ static numCallsToConstructor: number;
4
+ static callsToRun: string[];
5
+ run(dirPath: string): Promise<void>;
6
+ static resetTestDouble(): void;
7
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ class FakeNeurodevsAutocloner {
4
+ async run(dirPath) {
5
+ FakeNeurodevsAutocloner.callsToRun.push(dirPath);
6
+ }
7
+ static resetTestDouble() {
8
+ FakeNeurodevsAutocloner.numCallsToConstructor = 0;
9
+ FakeNeurodevsAutocloner.callsToRun = [];
10
+ }
11
+ }
12
+ FakeNeurodevsAutocloner.numCallsToConstructor = 0;
13
+ FakeNeurodevsAutocloner.callsToRun = [];
14
+ exports.default = FakeNeurodevsAutocloner;
15
+ //# sourceMappingURL=FakeNeurodevsAutocloner.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"FakeNeurodevsAutocloner.js","sourceRoot":"","sources":["../../../src/testDoubles/PresetUrlsAutocloner/FakeNeurodevsAutocloner.ts"],"names":[],"mappings":";;AAEA,MAAqB,uBAAuB;IAIjC,KAAK,CAAC,GAAG,CAAC,OAAe;QAC5B,uBAAuB,CAAC,UAAU,CAAC,IAAI,CAAC,OAAO,CAAC,CAAA;IACpD,CAAC;IAEM,MAAM,CAAC,eAAe;QACzB,uBAAuB,CAAC,qBAAqB,GAAG,CAAC,CAAA;QACjD,uBAAuB,CAAC,UAAU,GAAG,EAAE,CAAA;IAC3C,CAAC;;AAVa,6CAAqB,GAAG,CAAC,CAAA;AACzB,kCAAU,GAAa,EAAE,CAAA;kBAFtB,uBAAuB"}
@@ -0,0 +1,5 @@
1
+ export declare let callsToFakePrompts: Record<string, string>[][];
2
+ export declare function resetCallsToFakePrompts(): void;
3
+ export declare let fakeResponses: Record<string, string>;
4
+ export declare function setFakePromptsResponses(responses: Record<string, string>): void;
5
+ export declare function fakePrompts(questions: Record<string, string>[]): Record<string, string>;
@@ -0,0 +1,19 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.fakeResponses = exports.callsToFakePrompts = void 0;
4
+ exports.resetCallsToFakePrompts = resetCallsToFakePrompts;
5
+ exports.setFakePromptsResponses = setFakePromptsResponses;
6
+ exports.fakePrompts = fakePrompts;
7
+ exports.callsToFakePrompts = [];
8
+ function resetCallsToFakePrompts() {
9
+ exports.callsToFakePrompts = [];
10
+ }
11
+ exports.fakeResponses = {};
12
+ function setFakePromptsResponses(responses) {
13
+ exports.fakeResponses = responses;
14
+ }
15
+ function fakePrompts(questions) {
16
+ exports.callsToFakePrompts.push(questions);
17
+ return exports.fakeResponses;
18
+ }
19
+ //# sourceMappingURL=fakePrompts.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fakePrompts.js","sourceRoot":"","sources":["../../../src/testDoubles/prompts/fakePrompts.ts"],"names":[],"mappings":";;;AAEA,0DAEC;AAID,0DAEC;AAED,kCAGC;AAfU,QAAA,kBAAkB,GAA+B,EAAE,CAAA;AAE9D,SAAgB,uBAAuB;IACnC,0BAAkB,GAAG,EAAE,CAAA;AAC3B,CAAC;AAEU,QAAA,aAAa,GAA2B,EAAE,CAAA;AAErD,SAAgB,uBAAuB,CAAC,SAAiC;IACrE,qBAAa,GAAG,SAAS,CAAA;AAC7B,CAAC;AAED,SAAgB,WAAW,CAAC,SAAmC;IAC3D,0BAAkB,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;IAClC,OAAO,qBAAa,CAAA;AACxB,CAAC"}
@@ -0,0 +1,3 @@
1
+ import eslintConfigSpruce from 'eslint-config-spruce'
2
+
3
+ export default eslintConfigSpruce
package/package.json ADDED
@@ -0,0 +1,71 @@
1
+ {
2
+ "name": "@neurodevs/ndx-cli",
3
+ "version": "0.0.1",
4
+ "main": "build/index.js",
5
+ "repository": "https://github.com/neurodevs/ndx-cli.git",
6
+ "license": "MIT",
7
+ "bin": {
8
+ "ndx": "build/cli.js"
9
+ },
10
+ "scripts": {
11
+ "build.ci": "yarn run build.tsc && yarn run build.resolve-paths && yarn run lint",
12
+ "build.dev": "yarn run build.tsc --sourceMap ; yarn run resolve-paths.lint",
13
+ "build.copy-files": "mkdir -p build && rsync -avzq --exclude='*.ts' ./src/ ./build/",
14
+ "build.resolve-paths": "resolve-path-aliases --target build --patterns '**/*.js,**/*.d.ts'",
15
+ "build.tsc": "yarn run build.copy-files && tsc",
16
+ "clean": "yarn run clean.build",
17
+ "clean.all": "yarn run clean.dependencies && yarn run clean.build",
18
+ "clean.build": "rm -rf build/",
19
+ "clean.dependencies": "rm -rf node_modules/ package-lock.json yarn.lock",
20
+ "fix.lint": "eslint --fix --cache '**/*.ts'",
21
+ "lint": "eslint --cache '**/*.ts'",
22
+ "lint.tsc": "tsc -p . --noEmit",
23
+ "post.watch.build": "yarn run build.copy-files && yarn run build.resolve-paths",
24
+ "rebuild": "yarn run clean.all && yarn install && yarn run build.dev",
25
+ "update.dependencies": "yarn run clean.dependencies && yarn",
26
+ "resolve-paths.lint": "yarn run build.resolve-paths ; yarn run lint",
27
+ "test": "jest",
28
+ "watch.build.dev": "tsc-watch --sourceMap --onCompilationComplete 'yarn run post.watch.build'",
29
+ "watch.rebuild": "yarn run clean.all && yarn install && yarn run watch.build.dev",
30
+ "watch.tsc": "tsc -w"
31
+ },
32
+ "dependencies": {
33
+ "@neurodevs/meta-node": "^0.1.0",
34
+ "prompts": "^2.4.2"
35
+ },
36
+ "devDependencies": {
37
+ "@sprucelabs/jest-json-reporter": "^9.0.35",
38
+ "@sprucelabs/resolve-path-aliases": "^3.0.18",
39
+ "@sprucelabs/test": "^10.0.14",
40
+ "@sprucelabs/test-utils": "^6.0.54",
41
+ "@types/node": "^24.2.1",
42
+ "@types/prompts": "^2.4.9",
43
+ "chokidar-cli": "^3.0.0",
44
+ "eslint": "^9.33.0",
45
+ "eslint-config-spruce": "^11.2.26",
46
+ "jest": "^30.0.5",
47
+ "jest-circus": "^30.0.5",
48
+ "prettier": "^3.6.2",
49
+ "ts-node": "^10.9.2",
50
+ "tsc-watch": "^7.1.1",
51
+ "typescript": "^5.9.2"
52
+ },
53
+ "jest": {
54
+ "testRunner": "jest-circus/runner",
55
+ "testEnvironment": "node",
56
+ "testTimeout": 120000,
57
+ "maxWorkers": 4,
58
+ "testPathIgnorePatterns": [
59
+ "<rootDir>/tmp/",
60
+ "<rootDir>/src/",
61
+ "<rootDir>/node_modules/",
62
+ "<rootDir>/build/__tests__/testDirsAndFiles/"
63
+ ],
64
+ "testMatch": [
65
+ "**/__tests__/**/*.test.js?(x)"
66
+ ],
67
+ "moduleNameMapper": {
68
+ "^#spruce/(.*)$": "<rootDir>/build/.spruce/$1"
69
+ }
70
+ }
71
+ }
@@ -0,0 +1,11 @@
1
+ {
2
+ "scriptUpdater": {
3
+ "skipped": []
4
+ },
5
+ "skipped": [
6
+ "skill"
7
+ ],
8
+ "installed": [
9
+ "test"
10
+ ]
11
+ }
@@ -0,0 +1,121 @@
1
+ import AbstractSpruceTest, {
2
+ test,
3
+ assert,
4
+ generateId,
5
+ } from '@sprucelabs/test-utils'
6
+ import { FakeAutomodule, NodeAutomodule } from '@neurodevs/meta-node'
7
+ import CliCommandRunner, { CommandRunner } from '../../modules/CliCommandRunner'
8
+ import {
9
+ callsToFakePrompts,
10
+ fakePrompts,
11
+ resetCallsToFakePrompts,
12
+ setFakePromptsResponses,
13
+ } from '../../testDoubles/prompts/fakePrompts'
14
+
15
+ export default class CliCommandRunnerTest extends AbstractSpruceTest {
16
+ private static instance: CommandRunner
17
+
18
+ protected static async beforeEach() {
19
+ await super.beforeEach()
20
+
21
+ this.setFakeAutomodule()
22
+ this.setFakePrompts()
23
+
24
+ this.instance = this.CliCommandRunner()
25
+ }
26
+
27
+ @test()
28
+ protected static async createsInstance() {
29
+ assert.isTruthy(this.instance, 'Failed to create instance!')
30
+ }
31
+
32
+ @test()
33
+ protected static async throwsIfCommandIsNotSupported() {
34
+ const invalidArg = generateId()
35
+
36
+ const instance = this.CliCommandRunner([invalidArg])
37
+
38
+ const err = await assert.doesThrowAsync(
39
+ async () => await instance.run()
40
+ )
41
+
42
+ assert.isEqual(
43
+ err.message,
44
+ `The command "${invalidArg}" is not supported!`,
45
+ 'Did not receive the expected error!'
46
+ )
47
+ }
48
+
49
+ @test()
50
+ protected static async promptsUserForInput() {
51
+ await this.run()
52
+
53
+ assert.isEqualDeep(callsToFakePrompts[0], [
54
+ {
55
+ type: 'text',
56
+ name: 'interfaceName',
57
+ message: this.interfaceNameMessage,
58
+ },
59
+ {
60
+ type: 'text',
61
+ name: 'implName',
62
+ message: this.implNameMessage,
63
+ },
64
+ ])
65
+ }
66
+
67
+ @test()
68
+ protected static async createsNodeAutomodule() {
69
+ await this.run()
70
+
71
+ assert.isEqualDeep(FakeAutomodule.callsToConstructor[0], {
72
+ testSaveDir: 'src/__tests__/modules',
73
+ moduleSaveDir: 'src/modules',
74
+ interfaceName: this.interfaceName,
75
+ implName: this.implName,
76
+ })
77
+ }
78
+
79
+ @test()
80
+ protected static async callsRunOnNodeAutomodule() {
81
+ await this.run()
82
+
83
+ assert.isEqual(
84
+ FakeAutomodule.numCallsToRun,
85
+ 1,
86
+ 'Did not call run on Automodule!'
87
+ )
88
+ }
89
+
90
+ private static run() {
91
+ return this.instance.run()
92
+ }
93
+
94
+ private static setFakeAutomodule() {
95
+ NodeAutomodule.Class = FakeAutomodule
96
+ FakeAutomodule.resetTestDouble()
97
+ }
98
+
99
+ private static setFakePrompts() {
100
+ CliCommandRunner.prompts = fakePrompts as any
101
+ resetCallsToFakePrompts()
102
+
103
+ setFakePromptsResponses({
104
+ interfaceName: this.interfaceName,
105
+ implName: this.implName,
106
+ })
107
+ }
108
+
109
+ private static readonly interfaceName = generateId()
110
+ private static readonly implName = generateId()
111
+
112
+ private static readonly interfaceNameMessage =
113
+ 'What should the interface be called? Example: YourClass'
114
+
115
+ private static readonly implNameMessage =
116
+ 'What should the implementation class be called? Example: YourClassImpl'
117
+
118
+ private static CliCommandRunner(args?: string[]) {
119
+ return CliCommandRunner.Create(args ?? ['create.module'])
120
+ }
121
+ }
@@ -0,0 +1,94 @@
1
+ import AbstractSpruceTest, {
2
+ test,
3
+ assert,
4
+ generateId,
5
+ } from '@sprucelabs/test-utils'
6
+ import { FakeAutocloner, GitAutocloner } from '@neurodevs/meta-node'
7
+ import NeurodevsAutocloner, {
8
+ PresetUrlsAutocloner,
9
+ } from '../../modules/NeurodevsAutocloner'
10
+
11
+ export default class NeurodevsAutoclonerTest extends AbstractSpruceTest {
12
+ private static instance: PresetUrlsAutocloner
13
+
14
+ protected static async beforeEach() {
15
+ await super.beforeEach()
16
+
17
+ this.setFakeAutocloner()
18
+
19
+ this.instance = this.NeurodevsAutocloner()
20
+ }
21
+
22
+ @test()
23
+ protected static async canCreateNeurodevsAutocloner() {
24
+ assert.isTruthy(this.instance, 'Should create a new instance!')
25
+ }
26
+
27
+ @test()
28
+ protected static async createsGitAutocloner() {
29
+ assert.isEqual(
30
+ FakeAutocloner.numCallsToConstructor,
31
+ 1,
32
+ 'Should create a new instance of GitAutocloner!'
33
+ )
34
+ }
35
+
36
+ @test()
37
+ protected static async callsGitAutoclonerWithExpectedOptions() {
38
+ await this.instance.run(this.dirPath)
39
+
40
+ const options = FakeAutocloner.callsToRun[0]
41
+
42
+ assert.isEqualDeep(options, {
43
+ urls: this.repoUrls,
44
+ dirPath: this.dirPath,
45
+ })
46
+ }
47
+
48
+ private static repoNames = [
49
+ 'fili.js',
50
+ 'labrecorder',
51
+ 'liblsl',
52
+ 'libxdf',
53
+ 'node-autocloner',
54
+ 'node-autopackage',
55
+ 'node-autoupgrader',
56
+ 'node-biometrics',
57
+ 'node-biosensors',
58
+ 'node-biosignal-experiments',
59
+ 'node-ble',
60
+ 'node-csv',
61
+ 'node-eeg',
62
+ 'node-file-checker',
63
+ 'node-file-loader',
64
+ 'node-html-loader',
65
+ 'node-knowledge-graphs',
66
+ 'node-lsl',
67
+ 'node-mangled-names',
68
+ 'node-neuropype',
69
+ 'node-ppg',
70
+ 'node-server-plots',
71
+ 'node-signal-processing',
72
+ 'node-task-queue',
73
+ 'node-test-counter',
74
+ 'node-xdf',
75
+ 'personomic',
76
+ ]
77
+
78
+ private static generateUrl(repoName: string) {
79
+ return `https://github.com/neurodevs/${repoName}.git`
80
+ }
81
+
82
+ private static repoUrls = this.repoNames.map(this.generateUrl)
83
+
84
+ private static readonly dirPath = generateId()
85
+
86
+ private static setFakeAutocloner() {
87
+ GitAutocloner.Class = FakeAutocloner
88
+ FakeAutocloner.resetTestDouble()
89
+ }
90
+
91
+ private static NeurodevsAutocloner() {
92
+ return NeurodevsAutocloner.Create()
93
+ }
94
+ }
package/src/cli.ts ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+
3
+ import CliCommandRunner from './modules/CliCommandRunner'
4
+
5
+ async function main() {
6
+ const args = process.argv.slice(2)
7
+
8
+ const runner = CliCommandRunner.Create(args)
9
+ await runner.run()
10
+ }
11
+
12
+ main().catch((err) => {
13
+ console.error(err)
14
+ process.exit(1)
15
+ })
package/src/index.ts ADDED
@@ -0,0 +1 @@
1
+ //exports go here
@@ -0,0 +1,86 @@
1
+ import { NodeAutomodule } from '@neurodevs/meta-node'
2
+ import prompts from 'prompts'
3
+
4
+ export default class CliCommandRunner implements CommandRunner {
5
+ public static Class?: CommandRunnerConstructor
6
+ public static prompts = prompts
7
+
8
+ private args: string[]
9
+
10
+ private currentInterfaceName!: string
11
+ private currentImplName!: string
12
+
13
+ protected constructor(args: string[]) {
14
+ this.args = args
15
+ }
16
+
17
+ public static Create(args: string[]) {
18
+ return new (this.Class ?? this)(args)
19
+ }
20
+
21
+ public async run() {
22
+ this.throwIfCommandIsNotSupported()
23
+
24
+ const { interfaceName, implName } = await this.promptUserInput()
25
+
26
+ this.currentInterfaceName = interfaceName
27
+ this.currentImplName = implName
28
+
29
+ await this.createModule()
30
+ }
31
+
32
+ private throwIfCommandIsNotSupported() {
33
+ if (this.command !== 'create.module') {
34
+ throw new Error(`The command "${this.command}" is not supported!`)
35
+ }
36
+ }
37
+
38
+ private get command() {
39
+ return this.args[0]
40
+ }
41
+
42
+ private async promptUserInput() {
43
+ return await this.prompts([
44
+ {
45
+ type: 'text',
46
+ name: 'interfaceName',
47
+ message: this.interfaceNameMessage,
48
+ },
49
+ {
50
+ type: 'text',
51
+ name: 'implName',
52
+ message: this.implNameMessage,
53
+ },
54
+ ])
55
+ }
56
+
57
+ private readonly interfaceNameMessage =
58
+ 'What should the interface be called? Example: YourClass'
59
+
60
+ private readonly implNameMessage =
61
+ 'What should the implementation class be called? Example: YourClassImpl'
62
+
63
+ private get prompts() {
64
+ return CliCommandRunner.prompts
65
+ }
66
+
67
+ private async createModule() {
68
+ const automodule = this.NodeAutomodule()
69
+ await automodule.run()
70
+ }
71
+
72
+ private NodeAutomodule() {
73
+ return NodeAutomodule.Create({
74
+ testSaveDir: 'src/__tests__/modules',
75
+ moduleSaveDir: 'src/modules',
76
+ interfaceName: this.currentInterfaceName,
77
+ implName: this.currentImplName,
78
+ })
79
+ }
80
+ }
81
+
82
+ export interface CommandRunner {
83
+ run(): Promise<void>
84
+ }
85
+
86
+ export type CommandRunnerConstructor = new (args: string[]) => CommandRunner