slicejs-cli 3.3.0 → 3.4.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.
- package/AGENTS.md +247 -0
- package/LICENSE +21 -21
- package/client.js +663 -626
- package/commands/Print.js +163 -167
- package/commands/Validations.js +92 -103
- package/commands/build/build.js +40 -40
- package/commands/buildProduction/buildProduction.js +576 -579
- package/commands/bundle/bundle.js +234 -235
- package/commands/createComponent/VisualComponentTemplate.js +55 -55
- package/commands/createComponent/createComponent.js +124 -126
- package/commands/deleteComponent/deleteComponent.js +77 -77
- package/commands/doctor/doctor.js +366 -369
- package/commands/getComponent/getComponent.js +684 -747
- package/commands/init/init.js +269 -261
- package/commands/listComponents/listComponents.js +172 -175
- package/commands/startServer/startServer.js +261 -264
- package/commands/startServer/watchServer.js +79 -79
- package/commands/types/types.js +69 -27
- package/commands/utils/LocalCliDelegation.js +53 -53
- package/commands/utils/PathHelper.js +75 -68
- package/commands/utils/VersionChecker.js +167 -167
- package/commands/utils/bundling/BundleGenerator.js +2292 -2292
- package/commands/utils/bundling/DependencyAnalyzer.js +925 -933
- package/commands/utils/loadConfig.js +31 -0
- package/commands/utils/updateManager.js +452 -453
- package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +105 -105
- package/package.json +58 -46
- package/post.js +66 -65
- package/tests/bundle-generator.test.js +691 -708
- package/tests/bundle-v2-register-output.test.js +470 -470
- package/tests/client-launcher-contract.test.js +211 -211
- package/tests/client-update-flow-contract.test.js +272 -272
- package/tests/component-registry-parse.test.js +34 -0
- package/tests/dependency-analyzer.test.js +24 -24
- package/tests/fixtures/components.js +8 -0
- package/tests/fixtures/sliceConfig.json +74 -0
- package/tests/getcomponent.test.js +407 -0
- package/tests/helpers/setup.js +97 -0
- package/tests/init-command-contract.test.js +46 -0
- package/tests/local-cli-delegation.test.js +81 -79
- package/tests/path-helper.test.js +206 -0
- package/tests/types-breakage.test.js +491 -0
- package/tests/types-generator-errors.test.js +361 -0
- package/tests/types-generator.test.js +172 -184
- package/tests/update-manager-notifications.test.js +88 -88
- package/.github/workflows/docs-render-cicd.yml +0 -65
|
@@ -1,79 +1,81 @@
|
|
|
1
|
-
import { test } from 'node:test';
|
|
2
|
-
import assert from 'node:assert/strict';
|
|
3
|
-
import fs from 'node:fs';
|
|
4
|
-
import os from 'node:os';
|
|
5
|
-
import path from 'node:path';
|
|
6
|
-
import {
|
|
7
|
-
findNearestLocalCliEntry,
|
|
8
|
-
resolveLocalCliCandidate,
|
|
9
|
-
shouldDelegateToLocalCli,
|
|
10
|
-
isLocalDelegationDisabled
|
|
11
|
-
} from '../commands/utils/LocalCliDelegation.js';
|
|
12
|
-
|
|
13
|
-
test('isLocalDelegationDisabled returns true when env flag is set', () => {
|
|
14
|
-
const env = { SLICE_NO_LOCAL_DELEGATION: '1' };
|
|
15
|
-
assert.equal(isLocalDelegationDisabled(env), true);
|
|
16
|
-
});
|
|
17
|
-
|
|
18
|
-
test('isLocalDelegationDisabled returns false when env flag is missing', () => {
|
|
19
|
-
const env = {};
|
|
20
|
-
assert.equal(isLocalDelegationDisabled(env), false);
|
|
21
|
-
});
|
|
22
|
-
|
|
23
|
-
test('shouldDelegateToLocalCli is false when candidate is null', () => {
|
|
24
|
-
assert.equal(shouldDelegateToLocalCli('/tmp/current/client.js', null), false);
|
|
25
|
-
});
|
|
26
|
-
|
|
27
|
-
test('shouldDelegateToLocalCli is false when candidate realpath equals current realpath', () => {
|
|
28
|
-
const same = '/tmp/current/client.js';
|
|
29
|
-
assert.equal(shouldDelegateToLocalCli(same, same), false);
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
test('shouldDelegateToLocalCli is true when candidate differs from current', () => {
|
|
33
|
-
const current = '/tmp/global/client.js';
|
|
34
|
-
const local = '/tmp/project/node_modules/slicejs-cli/client.js';
|
|
35
|
-
assert.equal(shouldDelegateToLocalCli(current, local), true);
|
|
36
|
-
});
|
|
37
|
-
|
|
38
|
-
test('findNearestLocalCliEntry returns null when no candidate resolver hits', () => {
|
|
39
|
-
const cwd = path.join('/tmp', 'slice-nonexistent-project');
|
|
40
|
-
const resolver = () => null;
|
|
41
|
-
const result = findNearestLocalCliEntry(cwd, resolver);
|
|
42
|
-
assert.equal(result, null);
|
|
43
|
-
});
|
|
44
|
-
|
|
45
|
-
test('findNearestLocalCliEntry returns first match while traversing upward', () => {
|
|
46
|
-
const cwd = '/repo/apps/web/src';
|
|
47
|
-
const
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
}
|
|
79
|
-
});
|
|
1
|
+
import { test } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import fs from 'node:fs';
|
|
4
|
+
import os from 'node:os';
|
|
5
|
+
import path from 'node:path';
|
|
6
|
+
import {
|
|
7
|
+
findNearestLocalCliEntry,
|
|
8
|
+
resolveLocalCliCandidate,
|
|
9
|
+
shouldDelegateToLocalCli,
|
|
10
|
+
isLocalDelegationDisabled
|
|
11
|
+
} from '../commands/utils/LocalCliDelegation.js';
|
|
12
|
+
|
|
13
|
+
test('isLocalDelegationDisabled returns true when env flag is set', () => {
|
|
14
|
+
const env = { SLICE_NO_LOCAL_DELEGATION: '1' };
|
|
15
|
+
assert.equal(isLocalDelegationDisabled(env), true);
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
test('isLocalDelegationDisabled returns false when env flag is missing', () => {
|
|
19
|
+
const env = {};
|
|
20
|
+
assert.equal(isLocalDelegationDisabled(env), false);
|
|
21
|
+
});
|
|
22
|
+
|
|
23
|
+
test('shouldDelegateToLocalCli is false when candidate is null', () => {
|
|
24
|
+
assert.equal(shouldDelegateToLocalCli('/tmp/current/client.js', null), false);
|
|
25
|
+
});
|
|
26
|
+
|
|
27
|
+
test('shouldDelegateToLocalCli is false when candidate realpath equals current realpath', () => {
|
|
28
|
+
const same = '/tmp/current/client.js';
|
|
29
|
+
assert.equal(shouldDelegateToLocalCli(same, same), false);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
test('shouldDelegateToLocalCli is true when candidate differs from current', () => {
|
|
33
|
+
const current = '/tmp/global/client.js';
|
|
34
|
+
const local = '/tmp/project/node_modules/slicejs-cli/client.js';
|
|
35
|
+
assert.equal(shouldDelegateToLocalCli(current, local), true);
|
|
36
|
+
});
|
|
37
|
+
|
|
38
|
+
test('findNearestLocalCliEntry returns null when no candidate resolver hits', () => {
|
|
39
|
+
const cwd = path.join('/tmp', 'slice-nonexistent-project');
|
|
40
|
+
const resolver = () => null;
|
|
41
|
+
const result = findNearestLocalCliEntry(cwd, resolver);
|
|
42
|
+
assert.equal(result, null);
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
test('findNearestLocalCliEntry returns first match while traversing upward', () => {
|
|
46
|
+
const cwd = path.resolve('/repo/apps/web/src');
|
|
47
|
+
const matchDir = path.resolve('/repo/apps/web');
|
|
48
|
+
const matchResult = path.resolve('/repo/apps/web/node_modules/slicejs-cli/client.js');
|
|
49
|
+
const calls = [];
|
|
50
|
+
const resolver = (dir) => {
|
|
51
|
+
calls.push(dir);
|
|
52
|
+
if (dir === matchDir) {
|
|
53
|
+
return matchResult;
|
|
54
|
+
}
|
|
55
|
+
return null;
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
const result = findNearestLocalCliEntry(cwd, resolver);
|
|
59
|
+
|
|
60
|
+
assert.equal(result, matchResult);
|
|
61
|
+
assert.deepEqual(calls, [cwd, matchDir]);
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
test('findNearestLocalCliEntry returns null when resolver is not a function', () => {
|
|
65
|
+
const result = findNearestLocalCliEntry('/repo/apps/web/src', null);
|
|
66
|
+
assert.equal(result, null);
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
test('resolveLocalCliCandidate returns null when candidate path is a directory', () => {
|
|
70
|
+
const tempRoot = fs.mkdtempSync(path.join(os.tmpdir(), 'slice-cli-delegation-'));
|
|
71
|
+
const candidateDirectory = path.join(tempRoot, 'node_modules', 'slicejs-cli', 'client.js');
|
|
72
|
+
|
|
73
|
+
fs.mkdirSync(candidateDirectory, { recursive: true });
|
|
74
|
+
|
|
75
|
+
try {
|
|
76
|
+
const result = resolveLocalCliCandidate(tempRoot);
|
|
77
|
+
assert.equal(result, null);
|
|
78
|
+
} finally {
|
|
79
|
+
fs.rmSync(tempRoot, { recursive: true, force: true });
|
|
80
|
+
}
|
|
81
|
+
});
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
import { test, describe, before, after } from 'node:test';
|
|
2
|
+
import assert from 'node:assert/strict';
|
|
3
|
+
import path from 'node:path';
|
|
4
|
+
import fs from 'node:fs';
|
|
5
|
+
import { createTestProject, cleanupTestProject } from './helpers/setup.js';
|
|
6
|
+
|
|
7
|
+
/** @type {string} */
|
|
8
|
+
let tmpRoot;
|
|
9
|
+
const origInitCwd = process.env.INIT_CWD;
|
|
10
|
+
|
|
11
|
+
before(async () => {
|
|
12
|
+
tmpRoot = await createTestProject();
|
|
13
|
+
process.env.INIT_CWD = tmpRoot;
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
after(async () => {
|
|
17
|
+
if (origInitCwd === undefined) {
|
|
18
|
+
delete process.env.INIT_CWD;
|
|
19
|
+
} else {
|
|
20
|
+
process.env.INIT_CWD = origInitCwd;
|
|
21
|
+
}
|
|
22
|
+
await cleanupTestProject(tmpRoot);
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
describe('getProjectRoot', () => {
|
|
26
|
+
test('resolves project root via INIT_CWD when pointing to a real project', async () => {
|
|
27
|
+
const { getProjectRoot } = await import('../commands/utils/PathHelper.js');
|
|
28
|
+
const root = getProjectRoot(import.meta.url);
|
|
29
|
+
assert.ok(root, 'should return a path');
|
|
30
|
+
assert.ok(fs.existsSync(root), `resolved path should exist: ${root}`);
|
|
31
|
+
assert.equal(path.resolve(root), path.resolve(tmpRoot));
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
test('resolves project root via cwd when INIT_CWD is not set', async () => {
|
|
35
|
+
const prev = process.env.INIT_CWD;
|
|
36
|
+
delete process.env.INIT_CWD;
|
|
37
|
+
try {
|
|
38
|
+
const { getProjectRoot } = await import('../commands/utils/PathHelper.js');
|
|
39
|
+
const root = getProjectRoot(import.meta.url);
|
|
40
|
+
assert.ok(root, 'should return a path');
|
|
41
|
+
assert.ok(fs.existsSync(root), `resolved path should exist: ${root}`);
|
|
42
|
+
} finally {
|
|
43
|
+
process.env.INIT_CWD = prev;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
describe('getSrcPath', () => {
|
|
49
|
+
test('returns src path relative to project root', async () => {
|
|
50
|
+
const { getSrcPath } = await import('../commands/utils/PathHelper.js');
|
|
51
|
+
const srcPath = getSrcPath(import.meta.url);
|
|
52
|
+
assert.ok(srcPath.endsWith('src'), `should end with 'src': ${srcPath}`);
|
|
53
|
+
assert.ok(fs.existsSync(srcPath), `src path should exist: ${srcPath}`);
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('accepts subpath segments', async () => {
|
|
57
|
+
const { getSrcPath } = await import('../commands/utils/PathHelper.js');
|
|
58
|
+
const result = getSrcPath(import.meta.url, 'Components', 'components.js');
|
|
59
|
+
assert.ok(result.endsWith('src/Components/components.js') || result.endsWith('src\\Components\\components.js'));
|
|
60
|
+
assert.ok(fs.existsSync(result), `file should exist: ${result}`);
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('getApiPath', () => {
|
|
65
|
+
test('returns api path relative to project root', async () => {
|
|
66
|
+
const { getApiPath } = await import('../commands/utils/PathHelper.js');
|
|
67
|
+
const apiPath = getApiPath(import.meta.url);
|
|
68
|
+
assert.ok(apiPath.endsWith('api'), `should end with 'api': ${apiPath}`);
|
|
69
|
+
assert.ok(fs.existsSync(apiPath), `api path should exist: ${apiPath}`);
|
|
70
|
+
});
|
|
71
|
+
|
|
72
|
+
test('appends subpath to api', async () => {
|
|
73
|
+
const { getApiPath } = await import('../commands/utils/PathHelper.js');
|
|
74
|
+
const result = getApiPath(import.meta.url, 'index.js');
|
|
75
|
+
assert.ok(result.endsWith('api/index.js') || result.endsWith('api\\index.js'));
|
|
76
|
+
});
|
|
77
|
+
});
|
|
78
|
+
|
|
79
|
+
describe('getDistPath', () => {
|
|
80
|
+
test('returns dist path relative to project root', async () => {
|
|
81
|
+
const { getDistPath } = await import('../commands/utils/PathHelper.js');
|
|
82
|
+
const distPath = getDistPath(import.meta.url);
|
|
83
|
+
assert.ok(distPath.endsWith('dist'), `should end with 'dist': ${distPath}`);
|
|
84
|
+
});
|
|
85
|
+
});
|
|
86
|
+
|
|
87
|
+
describe('getConfigPath', () => {
|
|
88
|
+
test('returns sliceConfig.json path', async () => {
|
|
89
|
+
const { getConfigPath } = await import('../commands/utils/PathHelper.js');
|
|
90
|
+
const configPath = getConfigPath(import.meta.url);
|
|
91
|
+
assert.ok(configPath.endsWith('sliceConfig.json'), `should end with 'sliceConfig.json': ${configPath}`);
|
|
92
|
+
assert.ok(fs.existsSync(configPath), `config file should exist: ${configPath}`);
|
|
93
|
+
});
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
describe('getComponentsJsPath', () => {
|
|
97
|
+
test('returns components.js path', async () => {
|
|
98
|
+
const { getComponentsJsPath } = await import('../commands/utils/PathHelper.js');
|
|
99
|
+
const compPath = getComponentsJsPath(import.meta.url);
|
|
100
|
+
assert.ok(compPath.endsWith('components.js'), `should end with 'components.js': ${compPath}`);
|
|
101
|
+
assert.ok(fs.existsSync(compPath), `components.js should exist: ${compPath}`);
|
|
102
|
+
});
|
|
103
|
+
});
|
|
104
|
+
|
|
105
|
+
describe('getPath', () => {
|
|
106
|
+
test('joins arbitrary segments to project root', async () => {
|
|
107
|
+
const { getPath } = await import('../commands/utils/PathHelper.js');
|
|
108
|
+
const result = getPath(import.meta.url, 'src', 'routes.js');
|
|
109
|
+
assert.ok(result.endsWith('routes.js'), `should end with 'routes.js': ${result}`);
|
|
110
|
+
assert.ok(fs.existsSync(result), `routes.js should exist: ${result}`);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('sanitizes leading slashes from segments', async () => {
|
|
114
|
+
const { getPath } = await import('../commands/utils/PathHelper.js');
|
|
115
|
+
const result = getPath(import.meta.url, '/src/', '/routes.js');
|
|
116
|
+
assert.ok(result.endsWith('routes.js'), `should handle sanitized segments: ${result}`);
|
|
117
|
+
});
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
describe('resolveProjectRoot (fallback via candidates)', () => {
|
|
121
|
+
test('uses candidates() when INIT_CWD is not a valid path', async () => {
|
|
122
|
+
const orig = process.env.INIT_CWD;
|
|
123
|
+
process.env.INIT_CWD = 'C:\\nonexistent\\path';
|
|
124
|
+
|
|
125
|
+
try {
|
|
126
|
+
const { getProjectRoot } = await import('../commands/utils/PathHelper.js');
|
|
127
|
+
const root = getProjectRoot(import.meta.url);
|
|
128
|
+
assert.ok(root, 'should return a path');
|
|
129
|
+
assert.ok(fs.existsSync(root), `fallback path should exist: ${root}`);
|
|
130
|
+
} finally {
|
|
131
|
+
process.env.INIT_CWD = orig;
|
|
132
|
+
}
|
|
133
|
+
});
|
|
134
|
+
});
|
|
135
|
+
|
|
136
|
+
describe('joinRoot', () => {
|
|
137
|
+
test('joins segments relative to explicit root path', async () => {
|
|
138
|
+
const { joinRoot } = await import('../commands/utils/PathHelper.js');
|
|
139
|
+
const root = path.resolve('/test/project');
|
|
140
|
+
const result = joinRoot(root, 'src', 'components');
|
|
141
|
+
assert.equal(result, path.join(root, 'src', 'components'));
|
|
142
|
+
});
|
|
143
|
+
|
|
144
|
+
test('sanitizes leading slashes from segments', async () => {
|
|
145
|
+
const { joinRoot } = await import('../commands/utils/PathHelper.js');
|
|
146
|
+
const root = path.resolve('/test/project');
|
|
147
|
+
const result = joinRoot(root, '/src', '/components');
|
|
148
|
+
assert.equal(result, path.join(root, 'src', 'components'));
|
|
149
|
+
});
|
|
150
|
+
|
|
151
|
+
test('handles single segment', async () => {
|
|
152
|
+
const { joinRoot } = await import('../commands/utils/PathHelper.js');
|
|
153
|
+
const root = path.resolve('/test/project');
|
|
154
|
+
const result = joinRoot(root, 'package.json');
|
|
155
|
+
assert.equal(result, path.join(root, 'package.json'));
|
|
156
|
+
});
|
|
157
|
+
|
|
158
|
+
test('handles empty segments', async () => {
|
|
159
|
+
const { joinRoot } = await import('../commands/utils/PathHelper.js');
|
|
160
|
+
const root = path.resolve('/test/project');
|
|
161
|
+
const result = joinRoot(root);
|
|
162
|
+
assert.equal(result, root);
|
|
163
|
+
});
|
|
164
|
+
});
|
|
165
|
+
|
|
166
|
+
describe('getConfigPath with explicit root', () => {
|
|
167
|
+
test('returns sliceConfig.json under explicit root', async () => {
|
|
168
|
+
const { getConfigPath } = await import('../commands/utils/PathHelper.js');
|
|
169
|
+
const explicitRoot = path.resolve('/custom/project');
|
|
170
|
+
const result = getConfigPath(import.meta.url, explicitRoot);
|
|
171
|
+
assert.equal(result, path.join(explicitRoot, 'src', 'sliceConfig.json'));
|
|
172
|
+
});
|
|
173
|
+
|
|
174
|
+
test('returns sliceConfig.json without root (auto-resolve)', async () => {
|
|
175
|
+
const { getConfigPath } = await import('../commands/utils/PathHelper.js');
|
|
176
|
+
const result = getConfigPath(import.meta.url);
|
|
177
|
+
assert.ok(result.endsWith('sliceConfig.json'));
|
|
178
|
+
assert.ok(fs.existsSync(result));
|
|
179
|
+
});
|
|
180
|
+
});
|
|
181
|
+
|
|
182
|
+
describe('getComponentsJsPath with explicit root', () => {
|
|
183
|
+
test('returns components.js under explicit root', async () => {
|
|
184
|
+
const { getComponentsJsPath } = await import('../commands/utils/PathHelper.js');
|
|
185
|
+
const explicitRoot = path.resolve('/custom/project');
|
|
186
|
+
const result = getComponentsJsPath(import.meta.url, explicitRoot);
|
|
187
|
+
assert.equal(result, path.join(explicitRoot, 'src', 'Components', 'components.js'));
|
|
188
|
+
});
|
|
189
|
+
|
|
190
|
+
test('returns components.js without root (auto-resolve)', async () => {
|
|
191
|
+
const { getComponentsJsPath } = await import('../commands/utils/PathHelper.js');
|
|
192
|
+
const result = getComponentsJsPath(import.meta.url);
|
|
193
|
+
assert.ok(result.endsWith('components.js'));
|
|
194
|
+
assert.ok(fs.existsSync(result));
|
|
195
|
+
});
|
|
196
|
+
});
|
|
197
|
+
|
|
198
|
+
describe('getSrcPath integration', () => {
|
|
199
|
+
test('getSrcPath matches joinRoot with src prefix', async () => {
|
|
200
|
+
const { getSrcPath, getProjectRoot, joinRoot } = await import('../commands/utils/PathHelper.js');
|
|
201
|
+
const root = getProjectRoot(import.meta.url);
|
|
202
|
+
const viaHelper = getSrcPath(import.meta.url, 'Components', 'components.js');
|
|
203
|
+
const viaJoinRoot = joinRoot(root, 'src', 'Components', 'components.js');
|
|
204
|
+
assert.equal(viaHelper, viaJoinRoot);
|
|
205
|
+
});
|
|
206
|
+
});
|