slicejs-cli 3.5.1 → 3.6.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 (75) hide show
  1. package/README.md +81 -26
  2. package/client.js +73 -23
  3. package/commands/buildProduction/buildProduction.js +6 -3
  4. package/commands/doctor/doctor.js +68 -3
  5. package/commands/getComponent/getComponent.js +33 -25
  6. package/commands/init/init.js +176 -49
  7. package/commands/utils/PackageManager.js +148 -0
  8. package/commands/utils/VersionChecker.js +6 -4
  9. package/commands/utils/sliceScripts.js +23 -0
  10. package/commands/utils/updateManager.js +54 -35
  11. package/package.json +12 -1
  12. package/post.js +13 -19
  13. package/.github/ISSUE_TEMPLATE/bug_report.md +0 -29
  14. package/.github/ISSUE_TEMPLATE/feature_request.md +0 -25
  15. package/.github/pull_request_template.md +0 -22
  16. package/.github/workflows/ci.yml +0 -43
  17. package/AGENTS.md +0 -247
  18. package/CODE_OF_CONDUCT.md +0 -126
  19. package/ECOSYSTEM.md +0 -9
  20. package/docs/superpowers/specs/2026-05-10-pwa-generate-design.md +0 -182
  21. package/playwright.config.js +0 -51
  22. package/tests/build-command-integration.test.js +0 -87
  23. package/tests/build-production-e2e.test.js +0 -140
  24. package/tests/builder-edge-cases.test.js +0 -322
  25. package/tests/bundle-generate-e2e.test.js +0 -115
  26. package/tests/bundle-generator.test.js +0 -691
  27. package/tests/bundle-v2-register-output.test.js +0 -470
  28. package/tests/bundling-dependency-edges.test.js +0 -127
  29. package/tests/bundling-imports-unit.test.js +0 -267
  30. package/tests/client-launcher-contract.test.js +0 -211
  31. package/tests/client-update-flow-contract.test.js +0 -272
  32. package/tests/commands-component-crud.test.js +0 -102
  33. package/tests/commands-doctor.test.js +0 -80
  34. package/tests/commands-version-checker.test.js +0 -37
  35. package/tests/component-registry-parse.test.js +0 -34
  36. package/tests/dependency-analyzer.test.js +0 -24
  37. package/tests/e2e/bundles.spec.js +0 -91
  38. package/tests/e2e/dependency-scenarios.spec.js +0 -56
  39. package/tests/e2e/fixtures/components/Service/FetchManager/FetchManager.js +0 -136
  40. package/tests/e2e/fixtures/components/Service/IndexedDbManager/IndexedDbManager.js +0 -149
  41. package/tests/e2e/fixtures/components/Service/LocalStorageManager/LocalStorageManager.js +0 -45
  42. package/tests/e2e/fixtures/components/Visual/Button/Button.css +0 -106
  43. package/tests/e2e/fixtures/components/Visual/Button/Button.html +0 -5
  44. package/tests/e2e/fixtures/components/Visual/Button/Button.js +0 -158
  45. package/tests/e2e/fixtures/components/Visual/Link/Link.js +0 -33
  46. package/tests/e2e/fixtures/components/Visual/Loading/Loading.css +0 -56
  47. package/tests/e2e/fixtures/components/Visual/Loading/Loading.html +0 -83
  48. package/tests/e2e/fixtures/components/Visual/Loading/Loading.js +0 -164
  49. package/tests/e2e/fixtures/components/Visual/MultiRoute/MultiRoute.js +0 -167
  50. package/tests/e2e/fixtures/components/Visual/Navbar/Navbar.css +0 -116
  51. package/tests/e2e/fixtures/components/Visual/Navbar/Navbar.html +0 -44
  52. package/tests/e2e/fixtures/components/Visual/Navbar/Navbar.js +0 -180
  53. package/tests/e2e/fixtures/components/Visual/NotFound/NotFound.js +0 -20
  54. package/tests/e2e/fixtures/components/Visual/Route/Route.js +0 -181
  55. package/tests/e2e/fixtures/components/registry.json +0 -12
  56. package/tests/e2e/fixtures/vendor-components.mjs +0 -65
  57. package/tests/e2e/navigation.spec.js +0 -44
  58. package/tests/e2e/render.spec.js +0 -34
  59. package/tests/e2e/serve.mjs +0 -264
  60. package/tests/e2e/shared-deps.spec.js +0 -61
  61. package/tests/e2e/unminified.spec.js +0 -33
  62. package/tests/e2e-serve.test.js +0 -148
  63. package/tests/fixtures/components.js +0 -8
  64. package/tests/fixtures/sliceConfig.json +0 -74
  65. package/tests/getcomponent.test.js +0 -407
  66. package/tests/helpers/setup.js +0 -102
  67. package/tests/init-command-contract.test.js +0 -46
  68. package/tests/local-cli-delegation.test.js +0 -81
  69. package/tests/path-helper.test.js +0 -206
  70. package/tests/perf-budget.test.js +0 -86
  71. package/tests/postinstall-command.test.js +0 -72
  72. package/tests/types-breakage.test.js +0 -491
  73. package/tests/types-generator-errors.test.js +0 -361
  74. package/tests/types-generator.test.js +0 -346
  75. package/tests/update-manager-notifications.test.js +0 -88
@@ -1,126 +0,0 @@
1
- # Contributor Covenant Code of Conduct
2
-
3
- ## Our Pledge
4
-
5
- We as members, contributors, and leaders pledge to make participation in our
6
- community a harassment-free experience for everyone, regardless of age, body
7
- size, visible or invisible disability, ethnicity, sex characteristics, gender
8
- identity and expression, level of experience, education, socio-economic status,
9
- nationality, personal appearance, race, caste, color, religion, or sexual
10
- identity and orientation.
11
-
12
- We pledge to act and interact in ways that contribute to an open, welcoming,
13
- diverse, inclusive, and healthy community.
14
-
15
- ## Our Standards
16
-
17
- Examples of behavior that contributes to a positive environment for our
18
- community include:
19
-
20
- * Demonstrating empathy and kindness toward other people
21
- * Being respectful of differing opinions, viewpoints, and experiences
22
- * Giving and gracefully accepting constructive feedback
23
- * Accepting responsibility and apologizing to those affected by our mistakes,
24
- and learning from the experience
25
- * Focusing on what is best not just for us as individuals, but for the
26
- overall community
27
-
28
- Examples of unacceptable behavior include:
29
-
30
- * The use of sexualized language or imagery, and sexual attention or
31
- advances of any kind
32
- * Trolling, insulting or derogatory comments, and personal or political attacks
33
- * Public or private harassment
34
- * Publishing others' private information, such as a physical or email
35
- address, without their explicit permission
36
- * Other conduct which could reasonably be considered inappropriate in a
37
- professional setting
38
-
39
- ## Enforcement Responsibilities
40
-
41
- Community leaders are responsible for clarifying and enforcing our standards of
42
- acceptable behavior and will take appropriate and fair corrective action in
43
- response to any behavior that they deem inappropriate, threatening, offensive,
44
- or harmful.
45
-
46
- Community leaders have the right and responsibility to remove, edit, or reject
47
- comments, commits, code, wiki edits, issues, and other contributions that are
48
- not aligned to this Code of Conduct, and will communicate reasons for moderation
49
- decisions when appropriate.
50
-
51
- ## Scope
52
-
53
- This Code of Conduct applies within all community spaces, and also applies when
54
- an individual is officially representing the community in public spaces.
55
- Examples of representing our community include using an official e-mail address,
56
- posting via an official social media account, or acting as an appointed
57
- representative at an online or offline event.
58
-
59
- ## Enforcement
60
-
61
- Instances of abusive, harassing, or otherwise unacceptable behavior may be
62
- reported to Victor Kneider at vkneider.dev@gmail.com or via LinkedIn
63
- (https://www.linkedin.com/in/vkneider/). All complaints will be reviewed and investigated
64
- promptly and fairly.
65
-
66
- All community leaders are obligated to respect the privacy and security of the
67
- reporter of any incident.
68
-
69
- ## Enforcement Guidelines
70
-
71
- Community leaders will follow these Community Impact Guidelines in determining
72
- the consequences for any action they deem in violation of this Code of Conduct:
73
-
74
- ### 1. Correction
75
-
76
- **Community Impact**: Use of inappropriate language or other behavior deemed
77
- unprofessional or unwelcome in the community.
78
-
79
- **Consequence**: A private, written warning from community leaders, providing
80
- clarity around the nature of the violation and an explanation of why the
81
- behavior was inappropriate. A public apology may be requested.
82
-
83
- ### 2. Warning
84
-
85
- **Community Impact**: A violation through a single incident or series of
86
- actions.
87
-
88
- **Consequence**: A warning with consequences for continued behavior. No
89
- interaction with the people involved, including unsolicited interaction with
90
- those enforcing the Code of Conduct, for a specified period of time. This
91
- includes avoiding interactions in community spaces as well as external channels
92
- like social media. Violating these terms may lead to a temporary or permanent
93
- ban.
94
-
95
- ### 3. Temporary Ban
96
-
97
- **Community Impact**: A serious violation of community standards, including
98
- sustained inappropriate behavior.
99
-
100
- **Consequence**: A temporary ban from any sort of interaction or public
101
- communication with the community for a specified period of time. No public or
102
- private interaction with the people involved, including unsolicited interaction
103
- with those enforcing the Code of Conduct, is allowed during this period.
104
- Violating these terms may lead to a permanent ban.
105
-
106
- ### 4. Permanent Ban
107
-
108
- **Community Impact**: Demonstrating a pattern of violation of community
109
- standards, including sustained inappropriate behavior, harassment of an
110
- individual, or aggression toward or disparagement of classes of individuals.
111
-
112
- **Consequence**: A permanent ban from any sort of public interaction within
113
- the community.
114
-
115
- ## Attribution
116
-
117
- This Code of Conduct is adapted from the [Contributor Covenant](https://www.contributor-covenant.org),
118
- version 2.1, available at
119
- https://www.contributor-covenant.org/version/2/1/code_of_conduct.html.
120
-
121
- Community Impact Guidelines were inspired by [Mozilla's code of conduct
122
- enforcement ladder](https://github.com/mozilla/diversity).
123
-
124
- For answers to common questions about this code of conduct, see the FAQ at
125
- https://www.contributor-covenant.org/faq. Translations are available at
126
- https://www.contributor-covenant.org/translations.
package/ECOSYSTEM.md DELETED
@@ -1,9 +0,0 @@
1
- # Slice.js Ecosystem
2
-
3
- | Repository | Description | URL |
4
- |---|---|---|
5
- | slice.js | Core web framework | https://github.com/VKneider/slice.js |
6
- | slice-cli | CLI tool for project management | https://github.com/VKneider/slicejs-cli |
7
- | slice.js_visual_library | Official visual components and docs | https://github.com/VKneider/slice.js_visual_library |
8
- | sliceDocs | Framework documentation site | https://github.com/VKneider/slicejs_docs |
9
- | PortfolioVK2 | Portfolio and demo app | https://github.com/VKneider/portfolio |
@@ -1,182 +0,0 @@
1
- # Design of `slice generate-pwa` (V1)
2
-
3
- ## Objective
4
-
5
- Add a dedicated CLI command, `slice generate-pwa`, that converts a Slice build into an offline-capable PWA, with configurable cache strategy and explicit backend domain exclusion to prevent accidental REST API caching.
6
-
7
- The command must be post-bundle, operate on `dist/`, and maintain a simple V1 experience.
8
-
9
- ## V1 Scope
10
-
11
- - New `slice generate-pwa` command.
12
- - Automatically run `build` before the PWA process.
13
- - Generate `manifest.json` in `dist/`.
14
- - Generate `sw.js` in `dist/`.
15
- - Register Service Worker in the entry HTML of `dist`.
16
- - Support strategies: `hybrid` (default), `offline-first`, `network-first`.
17
- - Persist and read configuration from `src/sliceConfig.json` in:
18
- - `pwa.cache.excludeDomains`.
19
- - Apply effective exclusion of `localhost` and `127.0.0.1` in development.
20
-
21
- ## Out of V1 Scope
22
-
23
- - Exclusion by paths or headers (`excludePaths`, `excludeHeaders`).
24
- - Advanced interactive UI for creating PWA icons.
25
- - Support for push notifications, background sync, or advanced runtime caching by API type.
26
- - Formal plugin system; left prepared for future evolution.
27
-
28
- ## Command UX
29
-
30
- ### Syntax
31
-
32
- ```bash
33
- slice generate-pwa
34
- slice generate-pwa --strategy hybrid
35
- slice generate-pwa --strategy offline-first
36
- slice generate-pwa --strategy network-first
37
- slice generate-pwa --name "My App" --short-name "MyApp"
38
- ```
39
-
40
- ### V1 Flags
41
-
42
- - `--strategy <hybrid|offline-first|network-first>` (default: `hybrid`)
43
- - `--name <string>`
44
- - `--short-name <string>`
45
-
46
- ### Execution flow
47
-
48
- 1. Run production build.
49
- 2. Read and normalize PWA configuration from `src/sliceConfig.json`.
50
- 3. Generate asset manifest for precache from `dist/`.
51
- 4. Generate `dist/manifest.json`.
52
- 5. Generate `dist/sw.js` with the selected strategy.
53
- 6. Inject (or ensure) SW registration in entry HTML of `dist`.
54
- 7. Print final summary:
55
- - strategy used,
56
- - number of precached assets,
57
- - effective excluded domains.
58
-
59
- ## Configuration in `sliceConfig.json`
60
-
61
- V1 minimal section:
62
-
63
- ```json
64
- {
65
- "pwa": {
66
- "cache": {
67
- "excludeDomains": []
68
- }
69
- }
70
- }
71
- ```
72
-
73
- Rules:
74
-
75
- - If `pwa` does not exist, the command creates the section without breaking existing configuration.
76
- - `excludeDomains` accepts exact hosts (e.g., `api.mydomain.com`).
77
- - In development execution, `localhost` and `127.0.0.1` are effectively added (not necessarily persisted).
78
-
79
- ## Proposed Architecture
80
-
81
- ### CLI Integration
82
-
83
- - Add command in `client.js`:
84
- - `generate-pwa`
85
- - `--strategy` option
86
- - name options for manifest
87
-
88
- ### New Modules
89
-
90
- - `commands/pwa/generatePwa.js`
91
- - Orchestrator of the complete flow.
92
- - `commands/pwa/ConfigResolver.js`
93
- - Reads/creates/normalizes `pwa.cache.excludeDomains`.
94
- - `commands/pwa/AssetManifestBuilder.js`
95
- - Iterates `dist/` and builds precache list.
96
- - `commands/pwa/ManifestGenerator.js`
97
- - Generates `manifest.json` with defaults and flag overrides.
98
- - `commands/pwa/ServiceWorkerGenerator.js`
99
- - Generates `sw.js` with selected strategy and exclusions.
100
-
101
- ## Cache Design
102
-
103
- ### Global Rules
104
-
105
- - Intercept only `GET` requests.
106
- - If the host is in `excludeDomains`, do a direct `fetch` (no cache).
107
- - Cache versioning by build id (timestamp or build hash).
108
- - On new SW activation, automatically clean old caches.
109
-
110
- ### Strategies
111
-
112
- - `hybrid` (default):
113
- - static assets -> `cache-first`.
114
- - HTML navigation -> `network-first` with offline fallback.
115
- - `offline-first`:
116
- - navigation + static -> `cache-first`.
117
- - background update when online.
118
- - `network-first`:
119
- - navigation -> `network-first`.
120
- - precached static assets as fallback.
121
-
122
- ## REST API and Security Handling
123
-
124
- To prevent unwanted backend caching:
125
-
126
- - Domain exclusion via `excludeDomains` (main V1 rule).
127
- - Limit runtime cache to frontend assets and navigation per strategy.
128
- - Do not cache methods other than `GET`.
129
-
130
- Result: client assets are accelerated offline, but the backend stays out of the cache via explicit configuration.
131
-
132
- ## Error handling
133
-
134
- - If build fails, abort `generate-pwa` with a clear message.
135
- - If `dist/` does not exist after build, abort with diagnostics.
136
- - If `sliceConfig.json` is invalid, show error with repair suggestion.
137
- - If SW registration cannot be injected into HTML, report warning and target path.
138
-
139
- ## Testing
140
-
141
- ### Unit tests
142
-
143
- - `ConfigResolver`:
144
- - creates `pwa.cache.excludeDomains` section when it does not exist,
145
- - respects existing config.
146
- - `AssetManifestBuilder`:
147
- - includes expected assets,
148
- - excludes unsuitable files.
149
- - `ServiceWorkerGenerator`:
150
- - generates correct logic per strategy,
151
- - respects `excludeDomains`.
152
-
153
- ### Integration
154
-
155
- - `slice generate-pwa` runs build and creates `dist/manifest.json` + `dist/sw.js`.
156
- - SW registration present in output HTML.
157
- - domain exclusions applied in generated code.
158
-
159
- ### Minimal E2E manual
160
-
161
- - Build + generate-pwa.
162
- - Open app, validate installability (manifest).
163
- - Turn off network, validate offline navigation in `hybrid`.
164
- - Verify that requests to excluded domain are not served from SW cache.
165
-
166
- ## Evolution Plan (post V1)
167
-
168
- - `excludePaths` and `excludeHeaders`.
169
- - Assisted PWA icon and shortcut support.
170
- - Per-route strategy (e.g., `/api/*` network-only).
171
- - Extract reusable postbundle pipeline for other features.
172
-
173
- ## Acceptance Criteria
174
-
175
- - Functional `slice generate-pwa` command exists.
176
- - Runs build before generating PWA artifacts.
177
- - Generates `manifest.json` and `sw.js` in `dist/`.
178
- - Registers SW in main output HTML.
179
- - `hybrid` is default with HTML `network-first` and offline fallback.
180
- - Reads/writes `pwa.cache.excludeDomains` in `src/sliceConfig.json`.
181
- - Excludes configured domains from runtime cache.
182
- - Shows a readable final summary to the user.
@@ -1,51 +0,0 @@
1
- import { defineConfig, devices } from '@playwright/test';
2
-
3
- const PORT = process.env.E2E_PORT ? Number(process.env.E2E_PORT) : 3210;
4
- const UNMIN_PORT = PORT + 4;
5
- const BASE_URL = `http://127.0.0.1:${PORT}`;
6
- const UNMIN_URL = `http://127.0.0.1:${UNMIN_PORT}`;
7
-
8
- export default defineConfig({
9
- testDir: './tests/e2e',
10
- testMatch: '**/*.spec.js',
11
- fullyParallel: false,
12
- workers: 1,
13
- timeout: 30_000,
14
- expect: { timeout: 10_000 },
15
- reporter: process.env.CI ? [['list'], ['html', { open: 'never' }]] : [['list']],
16
- use: {
17
- trace: 'retain-on-failure',
18
- },
19
- projects: [
20
- {
21
- name: 'chromium',
22
- testIgnore: '**/unminified.spec.js',
23
- use: { ...devices['Desktop Chrome'], baseURL: BASE_URL },
24
- },
25
- {
26
- name: 'chromium-unminified',
27
- testMatch: '**/unminified.spec.js',
28
- use: { ...devices['Desktop Chrome'], baseURL: UNMIN_URL },
29
- },
30
- ],
31
- webServer: [
32
- {
33
- command: 'node tests/e2e/serve.mjs',
34
- url: `${BASE_URL}/slice-env.json`,
35
- env: { E2E_PORT: String(PORT) },
36
- timeout: 120_000,
37
- reuseExistingServer: !process.env.CI,
38
- stdout: 'pipe',
39
- stderr: 'pipe',
40
- },
41
- {
42
- command: 'node tests/e2e/serve.mjs',
43
- url: `${UNMIN_URL}/slice-env.json`,
44
- env: { E2E_PORT: String(UNMIN_PORT), E2E_MINIFY: 'false' },
45
- timeout: 120_000,
46
- reuseExistingServer: !process.env.CI,
47
- stdout: 'pipe',
48
- stderr: 'pipe',
49
- },
50
- ],
51
- });
@@ -1,87 +0,0 @@
1
- import { test, describe } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import fs from 'fs-extra';
4
- import path from 'node:path';
5
- import { parse } from '@babel/parser';
6
- import { withTestProject } from './helpers/setup.js';
7
- import build from '../commands/build/build.js';
8
- import { cleanBundles, bundleInfo } from '../commands/bundle/bundle.js';
9
-
10
- const MODULE_URL = import.meta.url;
11
-
12
- describe('slice build (full pipeline: buildProduction + bundle -> dist)', () => {
13
- test('produces a dist/ with parseable bundles', async () => {
14
- await withTestProject(async (root) => {
15
- const ok = await build({ minify: false, obfuscate: false });
16
- assert.equal(ok, true);
17
-
18
- const dist = path.join(root, 'dist');
19
- assert.ok(await fs.pathExists(path.join(dist, 'App', 'index.js')));
20
-
21
- const bundlesDir = path.join(dist, 'bundles');
22
- assert.ok(await fs.pathExists(bundlesDir), 'dist/bundles should exist');
23
-
24
- const bundleFiles = (await fs.readdir(bundlesDir)).filter(
25
- (f) => f.startsWith('slice-bundle.') && f.endsWith('.js')
26
- );
27
- assert.ok(bundleFiles.length > 0, 'at least one bundle in dist/bundles');
28
-
29
- for (const f of bundleFiles) {
30
- const code = await fs.readFile(path.join(bundlesDir, f), 'utf8');
31
- assert.doesNotThrow(
32
- () => parse(code, { sourceType: 'module', plugins: ['jsx'] }),
33
- `dist bundle ${f} is not valid JS`
34
- );
35
- }
36
- });
37
- });
38
- });
39
-
40
- describe('bundle clean / info subcommands', () => {
41
- test('cleanBundles removes slice-bundle.* files and the config', async () => {
42
- await withTestProject(async (root) => {
43
- const src = path.join(root, 'src');
44
- await fs.writeFile(path.join(src, 'slice-bundle.critical.js'), '// x');
45
- await fs.writeFile(path.join(src, 'slice-bundle.home.js'), '// x');
46
- await fs.writeFile(path.join(src, 'bundle.config.json'), '{}');
47
-
48
- await cleanBundles();
49
-
50
- assert.equal(await fs.pathExists(path.join(src, 'slice-bundle.critical.js')), false);
51
- assert.equal(await fs.pathExists(path.join(src, 'slice-bundle.home.js')), false);
52
- assert.equal(await fs.pathExists(path.join(src, 'bundle.config.json')), false);
53
- });
54
- });
55
-
56
- test('cleanBundles is a no-op (no throw) when there are no bundles', async () => {
57
- await withTestProject(async () => {
58
- await assert.doesNotReject(() => cleanBundles());
59
- });
60
- });
61
-
62
- test('bundleInfo does not throw when a config exists', async () => {
63
- await withTestProject(async (root) => {
64
- const cfg = {
65
- version: '2.0.0',
66
- strategy: 'hybrid',
67
- generated: '2025-01-01T00:00:00.000Z',
68
- stats: {
69
- totalComponents: 3,
70
- totalRoutes: 2,
71
- sharedComponents: 1,
72
- sharedPercentage: 33,
73
- totalSize: 2048,
74
- },
75
- bundles: { critical: { components: ['A'], size: 1024 }, routes: {} },
76
- };
77
- await fs.writeJson(path.join(root, 'src', 'bundle.config.json'), cfg);
78
- await assert.doesNotReject(() => bundleInfo());
79
- });
80
- });
81
-
82
- test('bundleInfo warns (no throw) when config is missing', async () => {
83
- await withTestProject(async () => {
84
- await assert.doesNotReject(() => bundleInfo());
85
- });
86
- });
87
- });
@@ -1,140 +0,0 @@
1
- import { test, describe } from 'node:test';
2
- import assert from 'node:assert/strict';
3
- import fs from 'fs-extra';
4
- import path from 'node:path';
5
- import { parse } from '@babel/parser';
6
- import { withTestProject } from './helpers/setup.js';
7
- import buildProduction from '../commands/buildProduction/buildProduction.js';
8
-
9
- const MODULE_URL = import.meta.url;
10
-
11
- async function writeSrc(root, rel, content) {
12
- const p = path.join(root, 'src', rel);
13
- await fs.ensureDir(path.dirname(p));
14
- await fs.writeFile(p, content, 'utf8');
15
- return p;
16
- }
17
-
18
- describe('buildProduction end-to-end', () => {
19
- test('produces a dist/ that preserves the Slice.js critical files', async () => {
20
- await withTestProject(async (root) => {
21
- const ok = await buildProduction({ minify: false });
22
- assert.equal(ok, true, 'build should succeed on the starter project');
23
-
24
- const dist = path.join(root, 'dist');
25
- assert.ok(await fs.pathExists(path.join(dist, 'sliceConfig.json')));
26
- assert.ok(await fs.pathExists(path.join(dist, 'Components', 'components.js')));
27
- assert.ok(await fs.pathExists(path.join(dist, 'App', 'index.js')));
28
- });
29
- });
30
-
31
- test('sliceConfig.json is copied verbatim (never minified)', async () => {
32
- await withTestProject(async (root) => {
33
- await buildProduction({ minify: true });
34
- const srcCfg = await fs.readFile(path.join(root, 'src', 'sliceConfig.json'), 'utf8');
35
- const distCfg = await fs.readFile(path.join(root, 'dist', 'sliceConfig.json'), 'utf8');
36
- assert.equal(distCfg, srcCfg);
37
- });
38
- });
39
-
40
- test('components.js keeps its registry structure after minification', async () => {
41
- await withTestProject(async (root) => {
42
- await buildProduction({ minify: true });
43
- const built = await fs.readFile(path.join(root, 'dist', 'Components', 'components.js'), 'utf8');
44
- assert.match(built, /const components/);
45
- assert.match(built, /export default/);
46
- assert.doesNotThrow(() => parse(built, { sourceType: 'module' }));
47
- });
48
- });
49
-
50
- test('--no-minify copies JS byte-for-byte', async () => {
51
- await withTestProject(async (root) => {
52
- const original = 'export function probe() {\n return 1 + 1;\n}\n';
53
- await writeSrc(root, 'App/probe.js', original);
54
- await buildProduction({ minify: false });
55
- const built = await fs.readFile(path.join(root, 'dist', 'App', 'probe.js'), 'utf8');
56
- assert.equal(built, original);
57
- });
58
- });
59
-
60
- test('minification preserves reserved Slice identifiers', async () => {
61
- await withTestProject(async (root) => {
62
- await writeSrc(
63
- root,
64
- 'App/probe.js',
65
- `export function probe() {\n` +
66
- ` const aVeryLongLocalNameThatShouldBeMangled = slice.build('Foo');\n` +
67
- ` return aVeryLongLocalNameThatShouldBeMangled;\n` +
68
- `}\nclass Controller {}\n`
69
- );
70
- await buildProduction({ minify: true });
71
- const built = await fs.readFile(path.join(root, 'dist', 'App', 'probe.js'), 'utf8');
72
- assert.match(built, /slice\.build/, 'reserved global "slice" must survive minification');
73
- assert.match(built, /Controller/, 'reserved class name "Controller" must survive');
74
- assert.doesNotThrow(() => parse(built, { sourceType: 'module' }));
75
- });
76
- });
77
-
78
- test('CSS is minified (whitespace collapsed)', async () => {
79
- await withTestProject(async (root) => {
80
- await writeSrc(root, 'Styles/probe.css', '.a {\n color: red;\n margin: 0;\n}\n');
81
- await buildProduction({ minify: true });
82
- const built = await fs.readFile(path.join(root, 'dist', 'Styles', 'probe.css'), 'utf8');
83
- assert.ok(built.length < 30, `expected minified css, got: ${JSON.stringify(built)}`);
84
- assert.match(built, /\.a\{/);
85
- });
86
- });
87
-
88
- test('HTML minification preserves slice-* attributes', async () => {
89
- await withTestProject(async (root) => {
90
- await writeSrc(
91
- root,
92
- 'App/probe.html',
93
- '<!DOCTYPE html>\n<html>\n <body>\n <div slice-id="my-component" >hi</div>\n </body>\n</html>\n'
94
- );
95
- await buildProduction({ minify: true });
96
- const built = await fs.readFile(path.join(root, 'dist', 'App', 'probe.html'), 'utf8');
97
- assert.match(built, /slice-id="my-component"/, 'slice-* attribute must be preserved');
98
- });
99
- });
100
-
101
- describe('clean / skip-clean semantics', () => {
102
- test('a stale dist file is removed by default', async () => {
103
- await withTestProject(async (root) => {
104
- const stale = path.join(root, 'dist', 'STALE_ARTIFACT.txt');
105
- await fs.ensureDir(path.dirname(stale));
106
- await fs.writeFile(stale, 'old');
107
- await buildProduction({ minify: false });
108
- assert.equal(await fs.pathExists(stale), false, 'stale dist file should be cleaned');
109
- });
110
- });
111
-
112
- test('--skip-clean keeps a stale dist file', async () => {
113
- await withTestProject(async (root) => {
114
- const stale = path.join(root, 'dist', 'STALE_ARTIFACT.txt');
115
- await fs.ensureDir(path.dirname(stale));
116
- await fs.writeFile(stale, 'old');
117
- await buildProduction({ minify: false, skipClean: true });
118
- assert.equal(await fs.pathExists(stale), true, 'stale dist file should survive --skip-clean');
119
- });
120
- });
121
- });
122
-
123
- test('build fails (returns false) when a critical file is missing', async () => {
124
- await withTestProject(async (root) => {
125
- await fs.remove(path.join(root, 'src', 'App', 'index.js'));
126
- const ok = await buildProduction({ minify: false });
127
- assert.equal(ok, false, 'missing App/index.js must abort the build');
128
- });
129
- });
130
-
131
- test('bundle.config inside a bundles/ folder is renamed to bundle.build.config', async () => {
132
- await withTestProject(async (root) => {
133
- await writeSrc(root, 'bundles/bundle.config.json', '{"production":true}');
134
- await buildProduction({ minify: false });
135
- const dist = path.join(root, 'dist', 'bundles');
136
- assert.equal(await fs.pathExists(path.join(dist, 'bundle.build.config.json')), true);
137
- assert.equal(await fs.pathExists(path.join(dist, 'bundle.config.json')), false);
138
- });
139
- });
140
- });