slicejs-cli 3.2.0 โ†’ 3.4.0

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 (40) hide show
  1. package/.github/ISSUE_TEMPLATE/bug_report.md +29 -0
  2. package/.github/ISSUE_TEMPLATE/feature_request.md +25 -0
  3. package/.github/pull_request_template.md +22 -0
  4. package/CODE_OF_CONDUCT.md +126 -0
  5. package/ECOSYSTEM.md +9 -0
  6. package/LICENSE +21 -0
  7. package/README.md +171 -375
  8. package/client.js +664 -563
  9. package/commands/Print.js +167 -167
  10. package/commands/Validations.js +103 -103
  11. package/commands/build/build.js +40 -40
  12. package/commands/buildProduction/buildProduction.js +579 -579
  13. package/commands/bundle/bundle.js +235 -235
  14. package/commands/createComponent/VisualComponentTemplate.js +55 -55
  15. package/commands/createComponent/createComponent.js +126 -126
  16. package/commands/deleteComponent/deleteComponent.js +77 -77
  17. package/commands/doctor/doctor.js +369 -369
  18. package/commands/getComponent/getComponent.js +747 -747
  19. package/commands/init/init.js +265 -261
  20. package/commands/listComponents/listComponents.js +175 -175
  21. package/commands/startServer/startServer.js +264 -264
  22. package/commands/startServer/watchServer.js +79 -79
  23. package/commands/types/types.js +16 -9
  24. package/commands/utils/LocalCliDelegation.js +53 -53
  25. package/commands/utils/PathHelper.js +68 -68
  26. package/commands/utils/VersionChecker.js +167 -167
  27. package/commands/utils/bundling/BundleGenerator.js +2292 -2292
  28. package/commands/utils/bundling/DependencyAnalyzer.js +933 -933
  29. package/commands/utils/updateManager.js +453 -453
  30. package/package.json +46 -46
  31. package/post.js +66 -25
  32. package/tests/bundle-generator.test.js +708 -708
  33. package/tests/bundle-v2-register-output.test.js +470 -470
  34. package/tests/client-launcher-contract.test.js +211 -211
  35. package/tests/client-update-flow-contract.test.js +272 -272
  36. package/tests/dependency-analyzer.test.js +24 -24
  37. package/tests/local-cli-delegation.test.js +79 -79
  38. package/tests/postinstall-command.test.js +72 -0
  39. package/tests/update-manager-notifications.test.js +88 -88
  40. package/refactor.md +0 -271
@@ -1,79 +1,79 @@
1
- import chokidar from 'chokidar';
2
- import chalk from 'chalk';
3
- import Print from '../Print.js';
4
-
5
- /**
6
- * Configura el watcher para archivos del proyecto
7
- * @param {ChildProcess} serverProcess - Proceso del servidor
8
- * @returns {FSWatcher} - Watcher de chokidar
9
- */
10
- export default function setupWatcher(serverProcess, onRestart) {
11
- Print.info('Watch mode enabled - monitoring file changes...');
12
- Print.newLine();
13
-
14
- const watcher = chokidar.watch(['src/**/*', 'api/**/*'], {
15
- ignored: [
16
- /(^|[\/\\])\../, // archivos ocultos
17
- '**/node_modules/**',
18
- '**/dist/**',
19
- '**/bundles/**',
20
- '**/*.log'
21
- ],
22
- persistent: true,
23
- ignoreInitial: true,
24
- awaitWriteFinish: {
25
- stabilityThreshold: 100,
26
- pollInterval: 50
27
- }
28
- });
29
-
30
- let reloadTimeout;
31
-
32
- watcher
33
- .on('change', (path) => {
34
- // Debounce para evitar mรบltiples reloads
35
- clearTimeout(reloadTimeout);
36
- reloadTimeout = setTimeout(() => {
37
- if(onRestart) {
38
- console.log(chalk.yellow('๐Ÿ”„ Changes detected, restarting server...'));
39
- onRestart(path);
40
- } else {
41
- console.log(chalk.yellow('๐Ÿ”„ Changes detected, server will reload automatically... (No handler)'));
42
- }
43
- }, 500);
44
- })
45
- .on('add', (path) => {
46
- // console.log(chalk.green(`โž• New file added: ${path}`));
47
- clearTimeout(reloadTimeout);
48
- reloadTimeout = setTimeout(() => {
49
- if (onRestart) onRestart(path);
50
- }, 500);
51
- })
52
- .on('unlink', (path) => {
53
- // console.log(chalk.red(`โž– File removed: ${path}`));
54
- clearTimeout(reloadTimeout);
55
- reloadTimeout = setTimeout(() => {
56
- if (onRestart) onRestart(path);
57
- }, 500);
58
- })
59
- .on('error', (error) => {
60
- Print.error(`Watcher error: ${error.message}`);
61
- })
62
- .on('ready', () => {
63
- console.log(chalk.gray('๐Ÿ‘€ Watching for file changes...'));
64
- Print.newLine();
65
- });
66
-
67
- return watcher;
68
- }
69
-
70
- /**
71
- * Detiene el watcher de forma segura
72
- * @param {FSWatcher} watcher - Watcher a detener
73
- */
74
- export function stopWatcher(watcher) {
75
- if (watcher) {
76
- watcher.close();
77
- console.log(chalk.gray('Watch mode stopped'));
78
- }
79
- }
1
+ import chokidar from 'chokidar';
2
+ import chalk from 'chalk';
3
+ import Print from '../Print.js';
4
+
5
+ /**
6
+ * Configura el watcher para archivos del proyecto
7
+ * @param {ChildProcess} serverProcess - Proceso del servidor
8
+ * @returns {FSWatcher} - Watcher de chokidar
9
+ */
10
+ export default function setupWatcher(serverProcess, onRestart) {
11
+ Print.info('Watch mode enabled - monitoring file changes...');
12
+ Print.newLine();
13
+
14
+ const watcher = chokidar.watch(['src/**/*', 'api/**/*'], {
15
+ ignored: [
16
+ /(^|[\/\\])\../, // archivos ocultos
17
+ '**/node_modules/**',
18
+ '**/dist/**',
19
+ '**/bundles/**',
20
+ '**/*.log'
21
+ ],
22
+ persistent: true,
23
+ ignoreInitial: true,
24
+ awaitWriteFinish: {
25
+ stabilityThreshold: 100,
26
+ pollInterval: 50
27
+ }
28
+ });
29
+
30
+ let reloadTimeout;
31
+
32
+ watcher
33
+ .on('change', (path) => {
34
+ // Debounce para evitar mรบltiples reloads
35
+ clearTimeout(reloadTimeout);
36
+ reloadTimeout = setTimeout(() => {
37
+ if(onRestart) {
38
+ console.log(chalk.yellow('๐Ÿ”„ Changes detected, restarting server...'));
39
+ onRestart(path);
40
+ } else {
41
+ console.log(chalk.yellow('๐Ÿ”„ Changes detected, server will reload automatically... (No handler)'));
42
+ }
43
+ }, 500);
44
+ })
45
+ .on('add', (path) => {
46
+ // console.log(chalk.green(`โž• New file added: ${path}`));
47
+ clearTimeout(reloadTimeout);
48
+ reloadTimeout = setTimeout(() => {
49
+ if (onRestart) onRestart(path);
50
+ }, 500);
51
+ })
52
+ .on('unlink', (path) => {
53
+ // console.log(chalk.red(`โž– File removed: ${path}`));
54
+ clearTimeout(reloadTimeout);
55
+ reloadTimeout = setTimeout(() => {
56
+ if (onRestart) onRestart(path);
57
+ }, 500);
58
+ })
59
+ .on('error', (error) => {
60
+ Print.error(`Watcher error: ${error.message}`);
61
+ })
62
+ .on('ready', () => {
63
+ console.log(chalk.gray('๐Ÿ‘€ Watching for file changes...'));
64
+ Print.newLine();
65
+ });
66
+
67
+ return watcher;
68
+ }
69
+
70
+ /**
71
+ * Detiene el watcher de forma segura
72
+ * @param {FSWatcher} watcher - Watcher a detener
73
+ */
74
+ export function stopWatcher(watcher) {
75
+ if (watcher) {
76
+ watcher.close();
77
+ console.log(chalk.gray('Watch mode stopped'));
78
+ }
79
+ }
@@ -427,13 +427,17 @@ const ensureEditorConfigForTypes = async ({ projectRoot, outputPath }) => {
427
427
  return `${dir}/**/*.d.ts`;
428
428
  })();
429
429
 
430
- if (!(await fs.pathExists(jsconfigPath))) {
430
+ const writeDefaultJsconfig = async () => {
431
431
  const jsconfig = {
432
432
  compilerOptions: { ...DEFAULT_EDITOR_COMPILER_OPTIONS },
433
433
  include: [...DEFAULT_EDITOR_INCLUDE, declarationGlob],
434
434
  exclude: [...DEFAULT_EDITOR_EXCLUDE]
435
435
  };
436
436
  await fs.writeFile(jsconfigPath, `${JSON.stringify(jsconfig, null, 2)}\n`, 'utf8');
437
+ };
438
+
439
+ if (!(await fs.pathExists(jsconfigPath))) {
440
+ await writeDefaultJsconfig();
437
441
  return { mode: 'created_jsconfig', filePath: jsconfigPath, includeAdded: true };
438
442
  }
439
443
 
@@ -441,14 +445,19 @@ const ensureEditorConfigForTypes = async ({ projectRoot, outputPath }) => {
441
445
  try {
442
446
  jsconfigRaw = await fs.readFile(jsconfigPath, 'utf8');
443
447
  } catch {
444
- return { mode: 'jsconfig_unreadable', filePath: jsconfigPath, includeAdded: false };
448
+ // Don't fail โ€” fall back to writing the default options.
449
+ await writeDefaultJsconfig();
450
+ return { mode: 'reset_jsconfig', reason: 'unreadable', filePath: jsconfigPath, includeAdded: true };
445
451
  }
446
452
 
447
453
  let parsed;
448
454
  try {
449
455
  parsed = JSON.parse(jsconfigRaw);
450
456
  } catch {
451
- return { mode: 'jsconfig_invalid_json', filePath: jsconfigPath, includeAdded: false };
457
+ // The jsconfig was edited into invalid JSON (a typo, comments, trailing commas...).
458
+ // Don't fail โ€” just write the default options so editor IntelliSense keeps working.
459
+ await writeDefaultJsconfig();
460
+ return { mode: 'reset_jsconfig', reason: 'invalid_json', filePath: jsconfigPath, includeAdded: true };
452
461
  }
453
462
 
454
463
  const include = Array.isArray(parsed.include) ? parsed.include : [];
@@ -513,12 +522,10 @@ const runGenerateTypes = async ({ projectRoot, outputPath }) => {
513
522
  Print.info(`jsconfig.json already includes declaration glob. Editor IntelliSense should pick generated types.`);
514
523
  } else if (editorConfig.mode === 'tsconfig_exists') {
515
524
  Print.info(`tsconfig.json detected. Types declaration is generated; ensure include covers ${toPosixRelative(projectRoot, outputPath)}.`);
516
- } else if (editorConfig.mode === 'jsconfig_invalid_json') {
517
- Print.warning(`Could not update jsconfig.json because it contains invalid JSON.`);
518
- Print.info(`Fix ${editorConfig.filePath} and run 'slice types generate' again.`);
519
- } else if (editorConfig.mode === 'jsconfig_unreadable') {
520
- Print.warning(`Could not read jsconfig.json to verify declaration include.`);
521
- Print.info(`Check permissions for ${editorConfig.filePath} and run 'slice types generate' again.`);
525
+ } else if (editorConfig.mode === 'reset_jsconfig') {
526
+ const why = editorConfig.reason === 'invalid_json' ? 'contained invalid JSON' : 'could not be read';
527
+ Print.warning(`jsconfig.json ${why}; wrote the default options so editor IntelliSense keeps working.`);
528
+ Print.info(`Review ${editorConfig.filePath} if you had custom settings there.`);
522
529
  } else if (editorConfig.mode === 'editor_config_error') {
523
530
  Print.warning(`Unexpected editor config setup error: ${editorConfig.errorMessage}`);
524
531
  }
@@ -1,53 +1,53 @@
1
- import fs from 'node:fs';
2
- import path from 'node:path';
3
-
4
- function getParentDirectory(dir) {
5
- const parent = path.dirname(dir);
6
- return parent === dir ? null : parent;
7
- }
8
-
9
- export function isLocalDelegationDisabled(env = process.env) {
10
- return env.SLICE_NO_LOCAL_DELEGATION === '1';
11
- }
12
-
13
- export function findNearestLocalCliEntry(startDirectory, resolveCandidate) {
14
- if (!startDirectory || typeof resolveCandidate !== 'function') {
15
- return null;
16
- }
17
-
18
- let current = path.resolve(startDirectory);
19
- while (current) {
20
- const candidate = resolveCandidate(current);
21
- if (candidate) {
22
- return candidate;
23
- }
24
- current = getParentDirectory(current);
25
- }
26
-
27
- return null;
28
- }
29
-
30
- export function resolveLocalCliCandidate(directory) {
31
- const candidate = path.join(directory, 'node_modules', 'slicejs-cli', 'client.js');
32
-
33
- try {
34
- const stats = fs.statSync(candidate);
35
- return stats.isFile() ? candidate : null;
36
- } catch {
37
- return null;
38
- }
39
- }
40
-
41
- export function shouldDelegateToLocalCli(currentEntryPath, localEntryPath) {
42
- if (!localEntryPath) {
43
- return false;
44
- }
45
-
46
- try {
47
- const currentReal = fs.realpathSync(currentEntryPath);
48
- const localReal = fs.realpathSync(localEntryPath);
49
- return currentReal !== localReal;
50
- } catch {
51
- return path.resolve(currentEntryPath) !== path.resolve(localEntryPath);
52
- }
53
- }
1
+ import fs from 'node:fs';
2
+ import path from 'node:path';
3
+
4
+ function getParentDirectory(dir) {
5
+ const parent = path.dirname(dir);
6
+ return parent === dir ? null : parent;
7
+ }
8
+
9
+ export function isLocalDelegationDisabled(env = process.env) {
10
+ return env.SLICE_NO_LOCAL_DELEGATION === '1';
11
+ }
12
+
13
+ export function findNearestLocalCliEntry(startDirectory, resolveCandidate) {
14
+ if (!startDirectory || typeof resolveCandidate !== 'function') {
15
+ return null;
16
+ }
17
+
18
+ let current = path.resolve(startDirectory);
19
+ while (current) {
20
+ const candidate = resolveCandidate(current);
21
+ if (candidate) {
22
+ return candidate;
23
+ }
24
+ current = getParentDirectory(current);
25
+ }
26
+
27
+ return null;
28
+ }
29
+
30
+ export function resolveLocalCliCandidate(directory) {
31
+ const candidate = path.join(directory, 'node_modules', 'slicejs-cli', 'client.js');
32
+
33
+ try {
34
+ const stats = fs.statSync(candidate);
35
+ return stats.isFile() ? candidate : null;
36
+ } catch {
37
+ return null;
38
+ }
39
+ }
40
+
41
+ export function shouldDelegateToLocalCli(currentEntryPath, localEntryPath) {
42
+ if (!localEntryPath) {
43
+ return false;
44
+ }
45
+
46
+ try {
47
+ const currentReal = fs.realpathSync(currentEntryPath);
48
+ const localReal = fs.realpathSync(localEntryPath);
49
+ return currentReal !== localReal;
50
+ } catch {
51
+ return path.resolve(currentEntryPath) !== path.resolve(localEntryPath);
52
+ }
53
+ }
@@ -1,68 +1,68 @@
1
- import path from 'path'
2
- import fs from 'fs-extra'
3
- import { fileURLToPath } from 'url'
4
-
5
- const sanitize = (s) => (s || '').replace(/^[/\\]+/, '')
6
- const dirOf = (url) => path.dirname(fileURLToPath(url))
7
-
8
- function candidates(moduleUrl) {
9
- const dir = dirOf(moduleUrl)
10
- return [
11
- path.join(dir, '../../'),
12
- path.join(dir, '../../../../')
13
- ]
14
- }
15
-
16
- function resolveProjectRoot(moduleUrl) {
17
- const initCwd = process.env.INIT_CWD
18
- if (initCwd && fs.pathExistsSync(initCwd)) {
19
- return initCwd
20
- }
21
-
22
- const cwd = process.cwd()
23
- if (cwd && fs.pathExistsSync(cwd)) {
24
- return cwd
25
- }
26
-
27
- const dirs = candidates(moduleUrl)
28
- for (const root of dirs) {
29
- const hasSrc = fs.pathExistsSync(path.join(root, 'src'))
30
- const hasApi = fs.pathExistsSync(path.join(root, 'api'))
31
- if (hasSrc || hasApi) return root
32
- }
33
- return dirs[1]
34
- }
35
-
36
- function joinProject(moduleUrl, ...segments) {
37
- const root = resolveProjectRoot(moduleUrl)
38
- const clean = segments.map(sanitize)
39
- return path.join(root, ...clean)
40
- }
41
-
42
- export function getProjectRoot(moduleUrl) {
43
- return resolveProjectRoot(moduleUrl)
44
- }
45
-
46
- export function getPath(moduleUrl, folder, ...segments) {
47
- return joinProject(moduleUrl, folder, ...segments)
48
- }
49
-
50
- export function getSrcPath(moduleUrl, ...segments) {
51
- return joinProject(moduleUrl, 'src', ...segments)
52
- }
53
-
54
- export function getApiPath(moduleUrl, ...segments) {
55
- return joinProject(moduleUrl, 'api', ...segments)
56
- }
57
-
58
- export function getDistPath(moduleUrl, ...segments) {
59
- return joinProject(moduleUrl, 'dist', ...segments)
60
- }
61
-
62
- export function getConfigPath(moduleUrl) {
63
- return joinProject(moduleUrl, 'src', 'sliceConfig.json')
64
- }
65
-
66
- export function getComponentsJsPath(moduleUrl) {
67
- return joinProject(moduleUrl, 'src', 'Components', 'components.js')
68
- }
1
+ import path from 'path'
2
+ import fs from 'fs-extra'
3
+ import { fileURLToPath } from 'url'
4
+
5
+ const sanitize = (s) => (s || '').replace(/^[/\\]+/, '')
6
+ const dirOf = (url) => path.dirname(fileURLToPath(url))
7
+
8
+ function candidates(moduleUrl) {
9
+ const dir = dirOf(moduleUrl)
10
+ return [
11
+ path.join(dir, '../../'),
12
+ path.join(dir, '../../../../')
13
+ ]
14
+ }
15
+
16
+ function resolveProjectRoot(moduleUrl) {
17
+ const initCwd = process.env.INIT_CWD
18
+ if (initCwd && fs.pathExistsSync(initCwd)) {
19
+ return initCwd
20
+ }
21
+
22
+ const cwd = process.cwd()
23
+ if (cwd && fs.pathExistsSync(cwd)) {
24
+ return cwd
25
+ }
26
+
27
+ const dirs = candidates(moduleUrl)
28
+ for (const root of dirs) {
29
+ const hasSrc = fs.pathExistsSync(path.join(root, 'src'))
30
+ const hasApi = fs.pathExistsSync(path.join(root, 'api'))
31
+ if (hasSrc || hasApi) return root
32
+ }
33
+ return dirs[1]
34
+ }
35
+
36
+ function joinProject(moduleUrl, ...segments) {
37
+ const root = resolveProjectRoot(moduleUrl)
38
+ const clean = segments.map(sanitize)
39
+ return path.join(root, ...clean)
40
+ }
41
+
42
+ export function getProjectRoot(moduleUrl) {
43
+ return resolveProjectRoot(moduleUrl)
44
+ }
45
+
46
+ export function getPath(moduleUrl, folder, ...segments) {
47
+ return joinProject(moduleUrl, folder, ...segments)
48
+ }
49
+
50
+ export function getSrcPath(moduleUrl, ...segments) {
51
+ return joinProject(moduleUrl, 'src', ...segments)
52
+ }
53
+
54
+ export function getApiPath(moduleUrl, ...segments) {
55
+ return joinProject(moduleUrl, 'api', ...segments)
56
+ }
57
+
58
+ export function getDistPath(moduleUrl, ...segments) {
59
+ return joinProject(moduleUrl, 'dist', ...segments)
60
+ }
61
+
62
+ export function getConfigPath(moduleUrl) {
63
+ return joinProject(moduleUrl, 'src', 'sliceConfig.json')
64
+ }
65
+
66
+ export function getComponentsJsPath(moduleUrl) {
67
+ return joinProject(moduleUrl, 'src', 'Components', 'components.js')
68
+ }