@rocket/js 0.1.0 → 0.1.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 (36) hide show
  1. package/README.md +14 -5
  2. package/dist-types/exports/components.d.ts +1 -1
  3. package/dist-types/exports/icons.d.ts +1 -1
  4. package/dist-types/exports/layout.d.ts +1 -1
  5. package/dist-types/src/cli/RocketBuild.d.ts.map +1 -1
  6. package/dist-types/src/cli/RocketInit.d.ts.map +1 -1
  7. package/dist-types/src/cli/RocketStart.d.ts +34 -2
  8. package/dist-types/src/cli/RocketStart.d.ts.map +1 -1
  9. package/dist-types/src/components.d.ts +2 -0
  10. package/dist-types/src/components.d.ts.map +1 -1
  11. package/dist-types/src/icons.d.ts +12 -0
  12. package/dist-types/src/icons.d.ts.map +1 -1
  13. package/dist-types/src/layouts/atlas/atlasDocLayout.d.ts.map +1 -1
  14. package/dist-types/src/layouts/atlas/atlasHeroLayout.d.ts.map +1 -1
  15. package/dist-types/src/layouts/layout.d.ts +2 -4
  16. package/dist-types/src/layouts/layout.d.ts.map +1 -1
  17. package/dist-types/src/standalone-demo-url.d.ts.map +1 -1
  18. package/dist-types/src/transform.d.ts.map +1 -1
  19. package/dist-types/src/wds-plugin.d.ts +1 -0
  20. package/dist-types/src/wds-plugin.d.ts.map +1 -1
  21. package/exports/components.js +1 -1
  22. package/exports/icons.js +3 -0
  23. package/exports/layout.js +1 -1
  24. package/package.json +1 -1
  25. package/src/cli/RocketBuild.js +38 -2
  26. package/src/cli/RocketInit.js +337 -24
  27. package/src/cli/RocketStart.js +96 -30
  28. package/src/components.js +19 -0
  29. package/src/icons.js +15 -0
  30. package/src/layouts/atlas/atlasDocLayout.js +2 -15
  31. package/src/layouts/atlas/atlasHeroLayout.js +3 -12
  32. package/src/layouts/layout.js +2 -12
  33. package/src/main.js +21 -4
  34. package/src/standalone-demo-url.js +21 -8
  35. package/src/transform.js +89 -2
  36. package/src/wds-plugin.js +14 -9
@@ -1,10 +1,24 @@
1
1
  /* eslint-disable no-console */
2
- import { existsSync, mkdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
2
+ import { existsSync, mkdirSync, readdirSync, readFileSync, statSync, writeFileSync } from 'node:fs';
3
3
  import path from 'node:path';
4
4
 
5
5
  const ROCKET_CONFIG_PATH = 'rocket-config.js';
6
6
  const INDEX_PAGE_PATH = 'docs/pages/index.rocket.md';
7
+ const SHARED_DATA_PATH = 'docs/pages/sharedData.js';
8
+ const DOCS_PAGE_PATH = 'docs/pages/docs.rocket.md';
9
+ const JAVASCRIPT_DEMO_PAGE_PATH = 'docs/pages/javascript-demo.rocket.md';
10
+ const REQUEST_DEMO_PAGE_PATH = 'docs/pages/request-demo.rocket.md';
11
+ const SITE_STATUS_PAGE_PATH = 'docs/pages/site-status.rocket.js';
7
12
  const ROCKET_AGENT_SKILL_PATH = '.agents/skills/rocket/SKILL.md';
13
+ const EXISTING_ROCKET_PAGE_NOISE_THRESHOLD = 3;
14
+ const STARTER_PAGE_FILES = [
15
+ SHARED_DATA_PATH,
16
+ INDEX_PAGE_PATH,
17
+ DOCS_PAGE_PATH,
18
+ JAVASCRIPT_DEMO_PAGE_PATH,
19
+ REQUEST_DEMO_PAGE_PATH,
20
+ SITE_STATUS_PAGE_PATH,
21
+ ];
8
22
 
9
23
  const rocketConfigSource = `/** @type {import('@rocket/js/types.js').RocketConfig} */
10
24
  export default {
@@ -12,6 +26,86 @@ export default {
12
26
  };
13
27
  `;
14
28
 
29
+ const sharedDataSource = `import { resolve } from '@rocket/js/resolve.js';
30
+
31
+ export const headerData = {
32
+ logo: [
33
+ resolve('@rocket/js/docs/assets/rocket-logo-light.svg', import.meta),
34
+ resolve('@rocket/js/docs/assets/rocket-text-no-logo.svg', import.meta),
35
+ ],
36
+ homeLink: '/',
37
+ navLinks: [
38
+ { text: 'Docs', href: '/docs' },
39
+ { text: 'Demos', href: '/javascript-demo' },
40
+ ],
41
+ socials: [],
42
+ };
43
+
44
+ /** @type {import('@rocket/js/types.js').FooterSection[]} */
45
+ export const footerData = [];
46
+
47
+ export const heroData = {
48
+ headerData,
49
+ footerData,
50
+ heroMainData: {
51
+ logoNoText: resolve('@rocket/js/docs/assets/rocket-logo-light.svg', import.meta),
52
+ eyebrow: 'ROCKET STARTER',
53
+ title: 'Rocket Site',
54
+ body: 'A static documentation site built with Rocket Pages and Atlas layouts.',
55
+ documentationLink: '/docs',
56
+ documentationText: 'Read the docs',
57
+ setupLink: '/javascript-demo',
58
+ setupText: 'View demos',
59
+ installLabel: 'Build',
60
+ installCommand: 'npm run build',
61
+ },
62
+ whyRocketData: [
63
+ {
64
+ icon: 'file-earmark-text',
65
+ title: 'Plain Pages',
66
+ description: 'Author durable content in Markdown and keep routes explicit.',
67
+ },
68
+ {
69
+ icon: 'play-btn',
70
+ title: 'Live Demos',
71
+ description: 'Use JavaScript Demos for browser examples with standalone URLs.',
72
+ },
73
+ {
74
+ icon: 'arrow-left-right',
75
+ title: 'Request Demos',
76
+ description: 'Show concrete same-site responses without leaving the docs.',
77
+ },
78
+ ],
79
+ quickStartData: {
80
+ title: 'Quick start',
81
+ subtitle: 'From this project:',
82
+ command: ['npm start', 'npm run build'],
83
+ description: 'Edit docs/pages, then run the build before publishing.',
84
+ },
85
+ workflowData: {
86
+ title: 'Starter workflow',
87
+ steps: [
88
+ {
89
+ icon: 'pencil-square',
90
+ title: 'Edit Pages',
91
+ description: 'Update the Markdown and JavaScript Pages under docs/pages.',
92
+ },
93
+ {
94
+ icon: 'terminal',
95
+ title: 'Build',
96
+ description: 'Run npm run build and fix any errors before publishing.',
97
+ },
98
+ ],
99
+ },
100
+ };
101
+
102
+ export const docData = {
103
+ headerData,
104
+ footerData,
105
+ navigationIconServerBudget: 35,
106
+ };
107
+ `;
108
+
15
109
  const indexPageSource = `\`\`\`js server
16
110
  export const config = {
17
111
  path: '/',
@@ -19,23 +113,165 @@ export const config = {
19
113
  title: 'Rocket Site',
20
114
  description: 'Documentation built with Rocket.',
21
115
  },
116
+ menu: {
117
+ iconName: 'house',
118
+ order: 0,
119
+ },
22
120
  };
23
121
 
24
- export { layout } from '@rocket/js/layout.js';
122
+ import { atlasHeroLayout, atlasHeroComponents } from '@rocket/js/layouts/atlasHero.js';
123
+ import { heroData } from './sharedData.js';
124
+
125
+ export const components = atlasHeroComponents;
126
+ export const layout = pageData => atlasHeroLayout(pageData, heroData);
25
127
  \`\`\`
26
128
 
27
129
  # Rocket Site
28
130
 
29
- This Page is rendered by Rocket.
131
+ This starter is rendered with Rocket's Atlas hero layout.
30
132
 
31
133
  ## Next steps
32
134
 
33
135
  - Edit this Page in \`docs/pages/index.rocket.md\`.
34
- - Add general documentation Pages under \`docs/pages\`.
35
- - Add component reference Pages next to the components they document.
136
+ - Edit the shared Atlas data in \`docs/pages/sharedData.js\`.
137
+ - Add general documentation Pages under \`docs/pages\` with the Atlas docs layout.
138
+ - Add component reference Pages next to the components they document under \`src\`.
36
139
  - Run \`npm run build\` to verify the site.
37
140
  `;
38
141
 
142
+ const docsPageSource = `\`\`\`js server
143
+ export const config = {
144
+ path: '/docs',
145
+ metadata: {
146
+ title: 'Docs',
147
+ description: 'A first Atlas docs Page generated by rocket init.',
148
+ },
149
+ menu: {
150
+ iconName: 'book',
151
+ order: 10,
152
+ },
153
+ };
154
+
155
+ import { atlasDocLayout, atlasDocComponents } from '@rocket/js/layouts/atlasDoc.js';
156
+ import { docData } from './sharedData.js';
157
+
158
+ export const components = atlasDocComponents;
159
+ export const layout = pageData => atlasDocLayout(pageData, docData);
160
+ \`\`\`
161
+
162
+ # Docs
163
+
164
+ This Page uses Rocket's package-provided Atlas docs layout.
165
+
166
+ ## Authoring Pages
167
+
168
+ Every Page owns its URL with \`config.path\`. General documentation Pages belong under
169
+ \`docs/pages\`, and component reference Pages can live next to the source files they document.
170
+
171
+ ## Navigation Icons
172
+
173
+ Atlas docs navigation reads \`menu.iconName\` from Page config. Use Bootstrap Icon names for Pages
174
+ that appear in the left navigation.
175
+ `;
176
+
177
+ const javascriptDemoPageSource = `\`\`\`js server
178
+ export const config = {
179
+ path: '/javascript-demo',
180
+ metadata: {
181
+ title: 'JavaScript Demo',
182
+ description: 'A starter JavaScript Demo with a generated Standalone Demo URL.',
183
+ },
184
+ menu: {
185
+ iconName: 'window',
186
+ order: 20,
187
+ },
188
+ };
189
+
190
+ import { atlasDocLayout, atlasDocComponents } from '@rocket/js/layouts/atlasDoc.js';
191
+ import { docData } from './sharedData.js';
192
+
193
+ export const components = atlasDocComponents;
194
+ export const layout = pageData => atlasDocLayout(pageData, docData);
195
+ \`\`\`
196
+
197
+ # JavaScript Demo
198
+
199
+ Use a \`js demo\` block for browser-rendered examples. Rocket also generates a Standalone Demo URL
200
+ at \`/javascript-demo/_demo/starterButton/\`.
201
+
202
+ \`\`\`js demo label="docs/pages/javascript-demo.rocket.md"
203
+ import { html } from 'lit';
204
+
205
+ export const starterButton = () => html\`<button type="button">Rocket demo</button>\`;
206
+ \`\`\`
207
+ `;
208
+
209
+ const requestDemoPageSource = `\`\`\`js server
210
+ export const config = {
211
+ path: '/request-demo',
212
+ metadata: {
213
+ title: 'Request Demo',
214
+ description: 'A starter Request Demo pointed at a concrete static JSON Page.',
215
+ },
216
+ menu: {
217
+ iconName: 'arrow-left-right',
218
+ order: 30,
219
+ },
220
+ };
221
+
222
+ import { atlasDocLayout, atlasDocComponents } from '@rocket/js/layouts/atlasDoc.js';
223
+ import { docData } from './sharedData.js';
224
+
225
+ export const components = atlasDocComponents;
226
+ export const layout = pageData => atlasDocLayout(pageData, docData);
227
+ \`\`\`
228
+
229
+ # Request Demo
230
+
231
+ Use a Request Demo when readers need to inspect a same-site \`GET\` response. Static Request Demo
232
+ examples should point at concrete paths and avoid query-dependent output.
233
+
234
+ \`\`\`js request-demo url="/api/site-status.json" label="docs/pages/site-status.rocket.js" height=220
235
+ export const config = {
236
+ path: '/api/site-status.json',
237
+ metadata: { title: 'Site Status' },
238
+ menu: false,
239
+ };
240
+
241
+ export default function siteStatusPage() {
242
+ return {
243
+ status: 'ready',
244
+ generatedBy: 'Rocket',
245
+ pages: ['/docs', '/javascript-demo', '/request-demo'],
246
+ };
247
+ }
248
+ \`\`\`
249
+ `;
250
+
251
+ const siteStatusPageSource = `export const config = {
252
+ path: '/api/site-status.json',
253
+ metadata: { title: 'Site Status' },
254
+ menu: false,
255
+ };
256
+
257
+ export default function siteStatusPage() {
258
+ return {
259
+ status: 'ready',
260
+ generatedBy: 'Rocket',
261
+ pages: ['/docs', '/javascript-demo', '/request-demo'],
262
+ };
263
+ }
264
+ `;
265
+
266
+ const STARTER_PAGE_SOURCES = new Map([
267
+ [SHARED_DATA_PATH, sharedDataSource],
268
+ [INDEX_PAGE_PATH, indexPageSource],
269
+ [DOCS_PAGE_PATH, docsPageSource],
270
+ [JAVASCRIPT_DEMO_PAGE_PATH, javascriptDemoPageSource],
271
+ [REQUEST_DEMO_PAGE_PATH, requestDemoPageSource],
272
+ [SITE_STATUS_PAGE_PATH, siteStatusPageSource],
273
+ ]);
274
+
39
275
  const rocketAgentSkillSource = `---
40
276
  name: rocket
41
277
  description: Use when editing Rocket Pages, config, layouts, component reference Pages, or build behavior in this project.
@@ -43,18 +279,21 @@ description: Use when editing Rocket Pages, config, layouts, component reference
43
279
 
44
280
  # Rocket
45
281
 
46
- Use this skill when working on this project's Rocket site.
47
-
48
282
  ## Rules
49
283
 
50
- - Read \`rocket-config.js\` before changing Pages.
51
- - Rocket discovers Pages from \`includeGlobs\`.
52
- - Every Page owns its public URL through \`config.path\`.
53
- - Put general documentation Pages under \`docs/pages\`.
54
- - Put component reference Pages next to the component they document.
55
- - Prefer Markdown Pages for durable content.
56
- - Use JavaScript Pages only for request-time or programmatic rendering.
57
- - Keep Rocket changes buildable with \`npm run build\`.
284
+ - Read \`rocket-config.js\` first; Page discovery follows \`includeGlobs\`.
285
+ - Every Page owns its URL through \`config.path\`; put general docs in \`docs/pages\` and component docs next to the component.
286
+ - Prefer Markdown for durable content; use JavaScript Pages for request-time or programmatic output.
287
+ - Prefer interactive examples for component and behavior docs; use \`js demo\` when readers benefit from trying the UI.
288
+ - Prefer Atlas docs layouts: \`atlasDocLayout\` for docs, \`atlasHeroLayout\` for a standalone docs home, with matching \`components\` exports.
289
+ - Markdown using Rocket custom elements needs a \`components\` export; use Atlas component maps or \`rocketDemoComponents\`.
290
+ - Add \`menu.iconName\` to Atlas docs navigation Pages so the left navigation has icons.
291
+ - Direct layout re-exports are supported when no local wrapper function is needed.
292
+ - Custom layouts rendering \`rocket-icon\` need \`addBootstrapIconLibrary(pageData)\` before \`document()\`.
293
+ - Static JavaScript Pages render once per concrete path; query/header/cookie/live-data output needs \`render: 'server'\`.
294
+ - Static Request Demos should target concrete non-query URLs.
295
+ - After adding a \`js demo\`, verify the parent Page and Standalone Demo URL \`/page/_demo/demoName/\`.
296
+ - Keep \`npm run build\` passing; record Rocket package issues separately from local workarounds.
58
297
  `;
59
298
 
60
299
  export class RocketInit {
@@ -64,7 +303,7 @@ export class RocketInit {
64
303
  async setupCommand(program) {
65
304
  program
66
305
  .command('init')
67
- .description('create a minimal Rocket project shape')
306
+ .description('create a Rocket docs starter')
68
307
  .action(() => {
69
308
  const result = this.init();
70
309
  reportInitResult(result);
@@ -89,8 +328,15 @@ export class RocketInit {
89
328
  writeJsonFile('package.json', packageJsonUpdate.packageJson);
90
329
  }
91
330
 
331
+ const shouldCreateStarterPages =
332
+ existingRocketPageFileCount(process.cwd()) < EXISTING_ROCKET_PAGE_NOISE_THRESHOLD;
333
+
92
334
  writeFileIfMissing(ROCKET_CONFIG_PATH, rocketConfigSource, result);
93
- writeFileIfMissing(INDEX_PAGE_PATH, indexPageSource, result);
335
+ if (shouldCreateStarterPages) {
336
+ for (const filePath of STARTER_PAGE_FILES) {
337
+ writeFileIfMissing(filePath, STARTER_PAGE_SOURCES.get(filePath) || '', result);
338
+ }
339
+ }
94
340
  writeFileIfMissing(ROCKET_AGENT_SKILL_PATH, rocketAgentSkillSource, result);
95
341
 
96
342
  return result;
@@ -204,7 +450,7 @@ function addRocketScript(scripts, { genericName, fallbackName, command, updated,
204
450
  updated.push(`scripts.${genericName}`);
205
451
  return;
206
452
  }
207
- if (scripts[genericName] === command) {
453
+ if (scriptRunsRocketCommand(scripts[genericName], genericName)) {
208
454
  skipped.push(`scripts.${genericName}`);
209
455
  return;
210
456
  }
@@ -237,8 +483,8 @@ function rocketNextSteps(packageJson) {
237
483
  return ['npx rocket start', 'npx rocket build'];
238
484
  }
239
485
  return [
240
- npmScriptCommand(packageJson.scripts, 'start', 'rocket:start', 'rocket start'),
241
- npmScriptCommand(packageJson.scripts, 'build', 'rocket:build', 'rocket build'),
486
+ npmScriptCommand(packageJson.scripts, 'start', 'rocket:start'),
487
+ npmScriptCommand(packageJson.scripts, 'build', 'rocket:build'),
242
488
  ];
243
489
  }
244
490
 
@@ -246,18 +492,85 @@ function rocketNextSteps(packageJson) {
246
492
  * @param {Record<string, any>} scripts
247
493
  * @param {string} genericName
248
494
  * @param {string} fallbackName
249
- * @param {string} command
250
495
  */
251
- function npmScriptCommand(scripts, genericName, fallbackName, command) {
252
- if (scripts[genericName] === command) {
496
+ function npmScriptCommand(scripts, genericName, fallbackName) {
497
+ if (scriptRunsRocketCommand(scripts[genericName], genericName)) {
253
498
  return genericName === 'start' ? 'npm start' : `npm run ${genericName}`;
254
499
  }
255
- if (scripts[fallbackName] === command) {
500
+ if (scriptRunsRocketCommand(scripts[fallbackName], genericName)) {
256
501
  return `npm run ${fallbackName}`;
257
502
  }
258
503
  return `npx rocket ${genericName}`;
259
504
  }
260
505
 
506
+ /**
507
+ * @param {unknown} script
508
+ * @param {string} commandName
509
+ * @returns {boolean}
510
+ */
511
+ function scriptRunsRocketCommand(script, commandName) {
512
+ if (typeof script !== 'string') {
513
+ return false;
514
+ }
515
+ const shellBoundary = '[\\s"\\\'`;&|()]';
516
+ return new RegExp(
517
+ `(^|${shellBoundary})(?:npx\\s+)?rocket\\s+${commandName}(?=$|${shellBoundary})`,
518
+ ).test(script);
519
+ }
520
+
521
+ /**
522
+ * @param {string} directory
523
+ * @returns {number}
524
+ */
525
+ function existingRocketPageFileCount(directory) {
526
+ if (!existsSync(directory)) {
527
+ return 0;
528
+ }
529
+ return countRocketPageFiles(directory, { count: 0 });
530
+ }
531
+
532
+ /**
533
+ * @param {string} directory
534
+ * @param {{ count: number }} state
535
+ * @returns {number}
536
+ */
537
+ function countRocketPageFiles(directory, state) {
538
+ if (state.count >= EXISTING_ROCKET_PAGE_NOISE_THRESHOLD) {
539
+ return state.count;
540
+ }
541
+ for (const entry of readdirSync(directory, { withFileTypes: true })) {
542
+ if (shouldIgnoreRocketPageScanEntry(entry.name)) {
543
+ continue;
544
+ }
545
+ const entryPath = path.join(directory, entry.name);
546
+ if (entry.isDirectory()) {
547
+ countRocketPageFiles(entryPath, state);
548
+ } else if (isRocketPageFile(entry.name)) {
549
+ state.count += 1;
550
+ }
551
+ if (state.count >= EXISTING_ROCKET_PAGE_NOISE_THRESHOLD) {
552
+ return state.count;
553
+ }
554
+ }
555
+ return state.count;
556
+ }
557
+
558
+ /**
559
+ * @param {string} name
560
+ * @returns {boolean}
561
+ */
562
+ function shouldIgnoreRocketPageScanEntry(name) {
563
+ return ['.git', '.netlify', 'coverage', 'dist', 'node_modules', 'temp'].includes(name);
564
+ }
565
+
566
+ /**
567
+ * @param {string} fileName
568
+ * @returns {boolean}
569
+ */
570
+ function isRocketPageFile(fileName) {
571
+ return fileName.endsWith('.rocket.md') || fileName.endsWith('.rocket.js');
572
+ }
573
+
261
574
  /**
262
575
  * @param {string} filePath
263
576
  * @param {string} contents
@@ -5,6 +5,9 @@ import { debounce } from '../debounce.js';
5
5
  import path from 'path';
6
6
 
7
7
  export class RocketStart {
8
+ /** @type {RocketStartOptions} */
9
+ startOptions = {};
10
+
8
11
  /**
9
12
  * @param {import('commander').Command} program
10
13
  * @param {import('./RocketCli.js').RocketCli} cli
@@ -12,17 +15,29 @@ export class RocketStart {
12
15
  async setupCommand(program, cli) {
13
16
  this.cli = cli;
14
17
 
15
- program.command('start').action(async () => {
16
- await cli.getConfig();
17
- await this.start();
18
- });
18
+ program
19
+ .command('start')
20
+ .option('-p, --port <port>', 'port for the development server')
21
+ .option('--no-open', 'do not open the browser')
22
+ .option('--no-watch', 'disable automatic file watching and reloads')
23
+ .action(async options => {
24
+ const startOptions = normalizeStartOptions(options);
25
+ await cli.getConfig();
26
+ await this.start(startOptions);
27
+ });
19
28
  }
20
29
 
21
- spawnServer() {
30
+ /**
31
+ * @param {RocketStartOptions} [options]
32
+ */
33
+ spawnServer(options = {}) {
22
34
  process.stdin.setRawMode?.(true);
23
35
  return spawn(
24
36
  'node',
25
- [path.join(import.meta.dirname, '../main.js'), this.cli?.configFilePath || ''],
37
+ startServerArgs({
38
+ configFilePath: this.cli?.configFilePath,
39
+ options,
40
+ }),
26
41
  {
27
42
  stdio: 'inherit',
28
43
  cwd: process.cwd(),
@@ -32,36 +47,42 @@ export class RocketStart {
32
47
 
33
48
  restartServer() {
34
49
  this.server?.kill();
35
- this.server = this.spawnServer();
50
+ this.server = this.spawnServer(this.startOptions);
36
51
  console.clear();
37
52
  console.log('Restarting rocket server...');
38
53
  }
39
54
 
40
- async start() {
41
- this.server = this.spawnServer();
55
+ /**
56
+ * @param {RocketStartOptions} [options]
57
+ */
58
+ async start(options = {}) {
59
+ this.startOptions = options;
60
+ this.server = this.spawnServer(options);
42
61
 
43
- // watch config
44
- watch(
45
- path.join(process.cwd(), this.cli?.configFilePath || 'rocket-config.js'),
46
- debounce(async (_, filename) => {
47
- if (!filename) {
48
- return;
49
- }
50
- this.restartServer();
51
- }, 100),
52
- );
62
+ if (options.watch !== false) {
63
+ // watch config
64
+ watch(
65
+ path.join(process.cwd(), this.cli?.configFilePath || 'rocket-config.js'),
66
+ debounce(async (_, filename) => {
67
+ if (!filename) {
68
+ return;
69
+ }
70
+ this.restartServer();
71
+ }, 100),
72
+ );
53
73
 
54
- // watch src
55
- watch(
56
- import.meta.dirname,
57
- { recursive: true },
58
- debounce(async (_, filename) => {
59
- if (!filename) {
60
- return;
61
- }
62
- this.restartServer();
63
- }, 100),
64
- );
74
+ // watch src
75
+ watch(
76
+ import.meta.dirname,
77
+ { recursive: true },
78
+ debounce(async (_, filename) => {
79
+ if (!filename) {
80
+ return;
81
+ }
82
+ this.restartServer();
83
+ }, 100),
84
+ );
85
+ }
65
86
 
66
87
  process.stdin.on('data', data => {
67
88
  const char = data.toString();
@@ -77,3 +98,48 @@ export class RocketStart {
77
98
  console.log('Restart the server with Ctrl+R');
78
99
  }
79
100
  }
101
+
102
+ /**
103
+ * @typedef {{ port?: number; open?: boolean; watch?: boolean }} RocketStartOptions
104
+ */
105
+
106
+ /**
107
+ * @param {{ port?: string; open?: boolean; watch?: boolean }} options
108
+ * @returns {RocketStartOptions}
109
+ */
110
+ export function normalizeStartOptions(options) {
111
+ return {
112
+ ...(options.port !== undefined ? { port: parsePortOption(options.port) } : {}),
113
+ ...(options.open !== undefined ? { open: options.open } : {}),
114
+ ...(options.watch !== undefined ? { watch: options.watch } : {}),
115
+ };
116
+ }
117
+
118
+ /**
119
+ * @param {{ configFilePath?: string; options?: RocketStartOptions }} input
120
+ */
121
+ export function startServerArgs({ configFilePath, options = {} }) {
122
+ return [
123
+ path.join(import.meta.dirname, '../main.js'),
124
+ configFilePath || '',
125
+ JSON.stringify(options),
126
+ ];
127
+ }
128
+
129
+ /**
130
+ * @param {string} value
131
+ */
132
+ function parsePortOption(value) {
133
+ if (!/^\d+$/.test(value)) {
134
+ throw new Error(
135
+ `Invalid --port ${JSON.stringify(value)}. Expected an integer from 1 to 65535.`,
136
+ );
137
+ }
138
+ const port = Number(value);
139
+ if (port < 1 || port > 65535) {
140
+ throw new Error(
141
+ `Invalid --port ${JSON.stringify(value)}. Expected an integer from 1 to 65535.`,
142
+ );
143
+ }
144
+ return port;
145
+ }
package/src/components.js CHANGED
@@ -1,6 +1,25 @@
1
1
  import { resolve } from '@rocket/js/resolve.js';
2
2
  import { createComponentHydration } from './component-hydration.js';
3
3
 
4
+ /** @type {import('@rocket/js/types.js').Components} */
5
+ export const rocketDemoComponents = {
6
+ 'rocket-code-block': {
7
+ file: './RocketCodeBlock.js',
8
+ className: 'RocketCodeBlock',
9
+ loading: 'hydrate:onVisible',
10
+ },
11
+ 'rocket-js-demo': {
12
+ file: './RocketJsDemo.js',
13
+ className: 'RocketJsDemo',
14
+ loading: 'client',
15
+ },
16
+ 'rocket-request-demo': {
17
+ file: './RocketRequestDemo.js',
18
+ className: 'RocketRequestDemo',
19
+ loading: 'client',
20
+ },
21
+ };
22
+
4
23
  /**
5
24
  * @param {import('@rocket/js/types.js').Components} components
6
25
  */
package/src/icons.js CHANGED
@@ -46,6 +46,21 @@ export function iconsFromPath(files) {
46
46
  };
47
47
  }
48
48
 
49
+ export const rocketBootstrapIconLibraries = {
50
+ bootstrap: iconsFromPackage('bootstrap-icons', 'icons/*.svg'),
51
+ };
52
+
53
+ export const rocketDefaultBootstrapIconLibrary = 'bootstrap';
54
+
55
+ /**
56
+ * @param {{ addIconLibraries: (iconLibraries: import('@rocket/js/types.js').IconLibrariesConfig, options?: { defaultIconLibrary?: string }) => void }} pageData
57
+ */
58
+ export function addBootstrapIconLibrary(pageData) {
59
+ pageData.addIconLibraries(rocketBootstrapIconLibraries, {
60
+ defaultIconLibrary: rocketDefaultBootstrapIconLibrary,
61
+ });
62
+ }
63
+
49
64
  export class IconAssetStore {
50
65
  constructor() {
51
66
  /** @type {Map<string, { url: string; svg: string; library: string; name: string }>} */
@@ -5,6 +5,7 @@ import { resolve } from '../../resolve.js';
5
5
  import { webAwesomeComponents } from '@rocket/js/components/web-awesome.js';
6
6
  import { addBootstrapIconLibrary } from '../layout.js';
7
7
  import { pageNavigationLinks } from '../../menus/pageNavigation.js';
8
+ import { rocketDemoComponents } from '../../components.js';
8
9
 
9
10
  export const DEFAULT_ATLAS_DOC_NAVIGATION_ICON_SERVER_BUDGET = 35;
10
11
 
@@ -35,21 +36,7 @@ export const atlasDocComponents = {
35
36
  className: 'RocketSocialLink',
36
37
  loading: 'server',
37
38
  },
38
- 'rocket-code-block': {
39
- file: './RocketCodeBlock.js',
40
- className: 'RocketCodeBlock',
41
- loading: 'hydrate:onVisible',
42
- },
43
- 'rocket-js-demo': {
44
- file: './RocketJsDemo.js',
45
- className: 'RocketJsDemo',
46
- loading: 'client',
47
- },
48
- 'rocket-request-demo': {
49
- file: './RocketRequestDemo.js',
50
- className: 'RocketRequestDemo',
51
- loading: 'client',
52
- },
39
+ ...rocketDemoComponents,
53
40
  ...webAwesomeComponents,
54
41
  };
55
42