@rocket/js 0.1.0 → 0.1.2
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/README.md +20 -26
- package/dist-types/exports/components.d.ts +1 -1
- package/dist-types/exports/icons.d.ts +1 -1
- package/dist-types/exports/layout.d.ts +1 -1
- package/dist-types/exports/types/rocket.d.ts +8 -0
- package/dist-types/exports/types/rocket.d.ts.map +1 -1
- package/dist-types/src/cli/RocketBuild.d.ts.map +1 -1
- package/dist-types/src/cli/RocketInit.d.ts +5 -1
- package/dist-types/src/cli/RocketInit.d.ts.map +1 -1
- package/dist-types/src/cli/RocketStart.d.ts +34 -2
- package/dist-types/src/cli/RocketStart.d.ts.map +1 -1
- package/dist-types/src/components.d.ts +2 -0
- package/dist-types/src/components.d.ts.map +1 -1
- package/dist-types/src/icons.d.ts +12 -0
- package/dist-types/src/icons.d.ts.map +1 -1
- package/dist-types/src/layouts/atlas/atlasDocLayout.d.ts +4 -0
- package/dist-types/src/layouts/atlas/atlasDocLayout.d.ts.map +1 -1
- package/dist-types/src/layouts/atlas/atlasHeroLayout.d.ts.map +1 -1
- package/dist-types/src/layouts/atlas/atlasNotFoundLayout.d.ts.map +1 -1
- package/dist-types/src/layouts/layout.d.ts +2 -4
- package/dist-types/src/layouts/layout.d.ts.map +1 -1
- package/dist-types/src/standalone-demo-url.d.ts.map +1 -1
- package/dist-types/src/transform.d.ts.map +1 -1
- package/dist-types/src/wds-plugin.d.ts +1 -0
- package/dist-types/src/wds-plugin.d.ts.map +1 -1
- package/exports/components.js +1 -1
- package/exports/icons.js +3 -0
- package/exports/layout.js +1 -1
- package/exports/types/rocket.ts +8 -0
- package/package.json +2 -2
- package/src/cli/RocketBuild.js +38 -2
- package/src/cli/RocketInit.js +401 -36
- package/src/cli/RocketStart.js +96 -30
- package/src/components.js +19 -0
- package/src/icons.js +15 -0
- package/src/layouts/atlas/atlasDocLayout.js +10 -15
- package/src/layouts/atlas/atlasHeroLayout.js +10 -13
- package/src/layouts/atlas/atlasNotFoundLayout.js +5 -3
- package/src/layouts/layout.js +2 -12
- package/src/main.js +21 -4
- package/src/standalone-demo-url.js +21 -8
- package/src/transform.js +89 -2
- package/src/wds-plugin.js +14 -9
package/src/cli/RocketInit.js
CHANGED
|
@@ -1,10 +1,26 @@
|
|
|
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 THEME_CSS_PATH = 'public/rocket-theme.css';
|
|
9
|
+
const DOCS_PAGE_PATH = 'docs/pages/docs.rocket.md';
|
|
10
|
+
const JAVASCRIPT_DEMO_PAGE_PATH = 'docs/pages/javascript-demo.rocket.md';
|
|
11
|
+
const REQUEST_DEMO_PAGE_PATH = 'docs/pages/request-demo.rocket.md';
|
|
12
|
+
const SITE_STATUS_PAGE_PATH = 'docs/pages/site-status.rocket.js';
|
|
7
13
|
const ROCKET_AGENT_SKILL_PATH = '.agents/skills/rocket/SKILL.md';
|
|
14
|
+
const EXISTING_ROCKET_PAGE_NOISE_THRESHOLD = 3;
|
|
15
|
+
const STARTER_PAGE_FILES = [
|
|
16
|
+
SHARED_DATA_PATH,
|
|
17
|
+
THEME_CSS_PATH,
|
|
18
|
+
INDEX_PAGE_PATH,
|
|
19
|
+
DOCS_PAGE_PATH,
|
|
20
|
+
JAVASCRIPT_DEMO_PAGE_PATH,
|
|
21
|
+
REQUEST_DEMO_PAGE_PATH,
|
|
22
|
+
SITE_STATUS_PAGE_PATH,
|
|
23
|
+
];
|
|
8
24
|
|
|
9
25
|
const rocketConfigSource = `/** @type {import('@rocket/js/types.js').RocketConfig} */
|
|
10
26
|
export default {
|
|
@@ -12,6 +28,108 @@ export default {
|
|
|
12
28
|
};
|
|
13
29
|
`;
|
|
14
30
|
|
|
31
|
+
const sharedDataSource = `import { resolve } from '@rocket/js/resolve.js';
|
|
32
|
+
|
|
33
|
+
const themeStylesheets = ['/rocket-theme.css'];
|
|
34
|
+
|
|
35
|
+
export const headerData = {
|
|
36
|
+
logo: [
|
|
37
|
+
resolve('@rocket/js/docs/assets/rocket-logo-light.svg', import.meta),
|
|
38
|
+
resolve('@rocket/js/docs/assets/rocket-text-no-logo.svg', import.meta),
|
|
39
|
+
],
|
|
40
|
+
homeLink: '/',
|
|
41
|
+
navLinks: [
|
|
42
|
+
{ text: 'Docs', href: '/docs' },
|
|
43
|
+
{ text: 'Demos', href: '/javascript-demo' },
|
|
44
|
+
],
|
|
45
|
+
socials: [],
|
|
46
|
+
};
|
|
47
|
+
|
|
48
|
+
/** @type {import('@rocket/js/types.js').FooterSection[]} */
|
|
49
|
+
export const footerData = [];
|
|
50
|
+
|
|
51
|
+
export const heroData = {
|
|
52
|
+
headerData,
|
|
53
|
+
footerData,
|
|
54
|
+
stylesheets: themeStylesheets,
|
|
55
|
+
heroMainData: {
|
|
56
|
+
logoNoText: resolve('@rocket/js/docs/assets/rocket-logo-light.svg', import.meta),
|
|
57
|
+
eyebrow: 'ROCKET STARTER',
|
|
58
|
+
title: 'Rocket Site',
|
|
59
|
+
body: 'A static documentation site built with Rocket Pages and Atlas layouts.',
|
|
60
|
+
documentationLink: '/docs',
|
|
61
|
+
documentationText: 'Read the docs',
|
|
62
|
+
setupLink: '/javascript-demo',
|
|
63
|
+
setupText: 'View demos',
|
|
64
|
+
installLabel: 'Build',
|
|
65
|
+
installCommand: 'npm run build',
|
|
66
|
+
},
|
|
67
|
+
whyRocketData: [
|
|
68
|
+
{
|
|
69
|
+
icon: 'file-earmark-text',
|
|
70
|
+
title: 'Plain Pages',
|
|
71
|
+
description: 'Author durable content in Markdown and keep routes explicit.',
|
|
72
|
+
},
|
|
73
|
+
{
|
|
74
|
+
icon: 'play-btn',
|
|
75
|
+
title: 'Live Demos',
|
|
76
|
+
description: 'Use JavaScript Demos for browser examples with standalone URLs.',
|
|
77
|
+
},
|
|
78
|
+
{
|
|
79
|
+
icon: 'arrow-left-right',
|
|
80
|
+
title: 'Request Demos',
|
|
81
|
+
description: 'Show concrete same-site responses without leaving the docs.',
|
|
82
|
+
},
|
|
83
|
+
],
|
|
84
|
+
quickStartData: {
|
|
85
|
+
title: 'Quick start',
|
|
86
|
+
subtitle: 'From this project:',
|
|
87
|
+
command: ['npm start', 'npm run build'],
|
|
88
|
+
description: 'Edit docs/pages, then run the build before publishing.',
|
|
89
|
+
},
|
|
90
|
+
workflowData: {
|
|
91
|
+
title: 'Starter workflow',
|
|
92
|
+
steps: [
|
|
93
|
+
{
|
|
94
|
+
icon: 'pencil-square',
|
|
95
|
+
title: 'Edit Pages',
|
|
96
|
+
description: 'Update the Markdown and JavaScript Pages under docs/pages.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
icon: 'terminal',
|
|
100
|
+
title: 'Build',
|
|
101
|
+
description: 'Run npm run build and fix any errors before publishing.',
|
|
102
|
+
},
|
|
103
|
+
],
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
|
|
107
|
+
export const docData = {
|
|
108
|
+
headerData,
|
|
109
|
+
footerData,
|
|
110
|
+
stylesheets: themeStylesheets,
|
|
111
|
+
navigationIconServerBudget: 35,
|
|
112
|
+
};
|
|
113
|
+
`;
|
|
114
|
+
|
|
115
|
+
const themeCssSource = `:root {
|
|
116
|
+
--rocket-theme-primary: #e10d14;
|
|
117
|
+
--rocket-theme-primary-dark: #b90f12;
|
|
118
|
+
--rocket-theme-link: #0366d6;
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
.atlas-page,
|
|
122
|
+
.atlas-header,
|
|
123
|
+
.atlas-navigation,
|
|
124
|
+
.atlas-toc,
|
|
125
|
+
.home-main,
|
|
126
|
+
rocket-header {
|
|
127
|
+
--primary-color: var(--rocket-theme-primary);
|
|
128
|
+
--primary-color-dark: var(--rocket-theme-primary-dark);
|
|
129
|
+
--link-color: var(--rocket-theme-link);
|
|
130
|
+
}
|
|
131
|
+
`;
|
|
132
|
+
|
|
15
133
|
const indexPageSource = `\`\`\`js server
|
|
16
134
|
export const config = {
|
|
17
135
|
path: '/',
|
|
@@ -19,23 +137,166 @@ export const config = {
|
|
|
19
137
|
title: 'Rocket Site',
|
|
20
138
|
description: 'Documentation built with Rocket.',
|
|
21
139
|
},
|
|
140
|
+
menu: {
|
|
141
|
+
iconName: 'house',
|
|
142
|
+
order: 0,
|
|
143
|
+
},
|
|
22
144
|
};
|
|
23
145
|
|
|
24
|
-
|
|
146
|
+
import { atlasHeroLayout, atlasHeroComponents } from '@rocket/js/layouts/atlasHero.js';
|
|
147
|
+
import { heroData } from './sharedData.js';
|
|
148
|
+
|
|
149
|
+
export const components = atlasHeroComponents;
|
|
150
|
+
export const layout = pageData => atlasHeroLayout(pageData, heroData);
|
|
25
151
|
\`\`\`
|
|
26
152
|
|
|
27
153
|
# Rocket Site
|
|
28
154
|
|
|
29
|
-
This
|
|
155
|
+
This starter is rendered with Rocket's Atlas hero layout.
|
|
30
156
|
|
|
31
157
|
## Next steps
|
|
32
158
|
|
|
33
159
|
- Edit this Page in \`docs/pages/index.rocket.md\`.
|
|
34
|
-
-
|
|
35
|
-
- Add
|
|
160
|
+
- Edit the shared Atlas data in \`docs/pages/sharedData.js\`.
|
|
161
|
+
- Add general documentation Pages under \`docs/pages\` with the Atlas docs layout.
|
|
162
|
+
- Add component reference Pages next to the components they document under \`src\`.
|
|
36
163
|
- Run \`npm run build\` to verify the site.
|
|
37
164
|
`;
|
|
38
165
|
|
|
166
|
+
const docsPageSource = `\`\`\`js server
|
|
167
|
+
export const config = {
|
|
168
|
+
path: '/docs',
|
|
169
|
+
metadata: {
|
|
170
|
+
title: 'Docs',
|
|
171
|
+
description: 'A first Atlas docs Page generated by rocket init.',
|
|
172
|
+
},
|
|
173
|
+
menu: {
|
|
174
|
+
iconName: 'book',
|
|
175
|
+
order: 10,
|
|
176
|
+
},
|
|
177
|
+
};
|
|
178
|
+
|
|
179
|
+
import { atlasDocLayout, atlasDocComponents } from '@rocket/js/layouts/atlasDoc.js';
|
|
180
|
+
import { docData } from './sharedData.js';
|
|
181
|
+
|
|
182
|
+
export const components = atlasDocComponents;
|
|
183
|
+
export const layout = pageData => atlasDocLayout(pageData, docData);
|
|
184
|
+
\`\`\`
|
|
185
|
+
|
|
186
|
+
# Docs
|
|
187
|
+
|
|
188
|
+
This Page uses Rocket's package-provided Atlas docs layout.
|
|
189
|
+
|
|
190
|
+
## Authoring Pages
|
|
191
|
+
|
|
192
|
+
Every Page owns its URL with \`config.path\`. General documentation Pages belong under
|
|
193
|
+
\`docs/pages\`, and component reference Pages can live next to the source files they document.
|
|
194
|
+
|
|
195
|
+
## Navigation Icons
|
|
196
|
+
|
|
197
|
+
Atlas docs navigation reads \`menu.iconName\` from Page config. Use Bootstrap Icon names for Pages
|
|
198
|
+
that appear in the left navigation.
|
|
199
|
+
`;
|
|
200
|
+
|
|
201
|
+
const javascriptDemoPageSource = `\`\`\`js server
|
|
202
|
+
export const config = {
|
|
203
|
+
path: '/javascript-demo',
|
|
204
|
+
metadata: {
|
|
205
|
+
title: 'JavaScript Demo',
|
|
206
|
+
description: 'A starter JavaScript Demo with a generated Standalone Demo URL.',
|
|
207
|
+
},
|
|
208
|
+
menu: {
|
|
209
|
+
iconName: 'window',
|
|
210
|
+
order: 20,
|
|
211
|
+
},
|
|
212
|
+
};
|
|
213
|
+
|
|
214
|
+
import { atlasDocLayout, atlasDocComponents } from '@rocket/js/layouts/atlasDoc.js';
|
|
215
|
+
import { docData } from './sharedData.js';
|
|
216
|
+
|
|
217
|
+
export const components = atlasDocComponents;
|
|
218
|
+
export const layout = pageData => atlasDocLayout(pageData, docData);
|
|
219
|
+
\`\`\`
|
|
220
|
+
|
|
221
|
+
# JavaScript Demo
|
|
222
|
+
|
|
223
|
+
Use a \`js demo\` block for browser-rendered examples. Rocket also generates a Standalone Demo URL
|
|
224
|
+
at \`/javascript-demo/_demo/starterButton/\`.
|
|
225
|
+
|
|
226
|
+
\`\`\`js demo label="docs/pages/javascript-demo.rocket.md"
|
|
227
|
+
import { html } from 'lit';
|
|
228
|
+
|
|
229
|
+
export const starterButton = () => html\`<button type="button">Rocket demo</button>\`;
|
|
230
|
+
\`\`\`
|
|
231
|
+
`;
|
|
232
|
+
|
|
233
|
+
const requestDemoPageSource = `\`\`\`js server
|
|
234
|
+
export const config = {
|
|
235
|
+
path: '/request-demo',
|
|
236
|
+
metadata: {
|
|
237
|
+
title: 'Request Demo',
|
|
238
|
+
description: 'A starter Request Demo pointed at a concrete static JSON Page.',
|
|
239
|
+
},
|
|
240
|
+
menu: {
|
|
241
|
+
iconName: 'arrow-left-right',
|
|
242
|
+
order: 30,
|
|
243
|
+
},
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
import { atlasDocLayout, atlasDocComponents } from '@rocket/js/layouts/atlasDoc.js';
|
|
247
|
+
import { docData } from './sharedData.js';
|
|
248
|
+
|
|
249
|
+
export const components = atlasDocComponents;
|
|
250
|
+
export const layout = pageData => atlasDocLayout(pageData, docData);
|
|
251
|
+
\`\`\`
|
|
252
|
+
|
|
253
|
+
# Request Demo
|
|
254
|
+
|
|
255
|
+
Use a Request Demo when readers need to inspect a same-site \`GET\` response. Static Request Demo
|
|
256
|
+
examples should point at concrete paths and avoid query-dependent output.
|
|
257
|
+
|
|
258
|
+
\`\`\`js request-demo url="/api/site-status.json" label="docs/pages/site-status.rocket.js" height=220
|
|
259
|
+
export const config = {
|
|
260
|
+
path: '/api/site-status.json',
|
|
261
|
+
metadata: { title: 'Site Status' },
|
|
262
|
+
menu: false,
|
|
263
|
+
};
|
|
264
|
+
|
|
265
|
+
export default function siteStatusPage() {
|
|
266
|
+
return {
|
|
267
|
+
status: 'ready',
|
|
268
|
+
generatedBy: 'Rocket',
|
|
269
|
+
pages: ['/docs', '/javascript-demo', '/request-demo'],
|
|
270
|
+
};
|
|
271
|
+
}
|
|
272
|
+
\`\`\`
|
|
273
|
+
`;
|
|
274
|
+
|
|
275
|
+
const siteStatusPageSource = `export const config = {
|
|
276
|
+
path: '/api/site-status.json',
|
|
277
|
+
metadata: { title: 'Site Status' },
|
|
278
|
+
menu: false,
|
|
279
|
+
};
|
|
280
|
+
|
|
281
|
+
export default function siteStatusPage() {
|
|
282
|
+
return {
|
|
283
|
+
status: 'ready',
|
|
284
|
+
generatedBy: 'Rocket',
|
|
285
|
+
pages: ['/docs', '/javascript-demo', '/request-demo'],
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
`;
|
|
289
|
+
|
|
290
|
+
const STARTER_PAGE_SOURCES = new Map([
|
|
291
|
+
[SHARED_DATA_PATH, sharedDataSource],
|
|
292
|
+
[THEME_CSS_PATH, themeCssSource],
|
|
293
|
+
[INDEX_PAGE_PATH, indexPageSource],
|
|
294
|
+
[DOCS_PAGE_PATH, docsPageSource],
|
|
295
|
+
[JAVASCRIPT_DEMO_PAGE_PATH, javascriptDemoPageSource],
|
|
296
|
+
[REQUEST_DEMO_PAGE_PATH, requestDemoPageSource],
|
|
297
|
+
[SITE_STATUS_PAGE_PATH, siteStatusPageSource],
|
|
298
|
+
]);
|
|
299
|
+
|
|
39
300
|
const rocketAgentSkillSource = `---
|
|
40
301
|
name: rocket
|
|
41
302
|
description: Use when editing Rocket Pages, config, layouts, component reference Pages, or build behavior in this project.
|
|
@@ -43,18 +304,26 @@ description: Use when editing Rocket Pages, config, layouts, component reference
|
|
|
43
304
|
|
|
44
305
|
# Rocket
|
|
45
306
|
|
|
46
|
-
Use this skill when working on this project's Rocket site.
|
|
47
|
-
|
|
48
307
|
## Rules
|
|
49
308
|
|
|
50
|
-
- Read \`rocket-config.js\`
|
|
51
|
-
-
|
|
52
|
-
-
|
|
53
|
-
-
|
|
54
|
-
-
|
|
55
|
-
-
|
|
56
|
-
-
|
|
57
|
-
-
|
|
309
|
+
- Read \`rocket-config.js\` first; Page discovery follows \`includeGlobs\`.
|
|
310
|
+
- Every Page owns its URL through \`config.path\`; put general docs in \`docs/pages\` and component docs next to the component.
|
|
311
|
+
- Prefer Markdown for durable content; use JavaScript Pages for request-time or programmatic output.
|
|
312
|
+
- Prefer interactive examples for component and behavior docs; use \`js demo\` when readers benefit from trying the UI.
|
|
313
|
+
- Prefer Atlas docs layouts: \`atlasDocLayout\` for docs, \`atlasHeroLayout\` for a standalone docs home, with matching \`components\` exports.
|
|
314
|
+
- Markdown using Rocket custom elements needs a \`components\` export; use Atlas component maps or \`rocketDemoComponents\`.
|
|
315
|
+
- Add \`menu.iconName\` to Atlas docs navigation Pages so the left navigation has icons.
|
|
316
|
+
- Direct layout re-exports are supported when no local wrapper function is needed.
|
|
317
|
+
- For Atlas theming, use shared layout data with \`stylesheets\` and centralized CSS variables instead of per-Page style injection.
|
|
318
|
+
- To add a general Page, create \`docs/pages/name.rocket.md\`, set \`config.path\`, \`metadata\`, \`menu.iconName\`, and use the shared docs layout.
|
|
319
|
+
- Custom layouts rendering \`rocket-icon\` need \`addBootstrapIconLibrary(pageData)\` before \`document()\`.
|
|
320
|
+
- Static JavaScript Pages render once per concrete path; query/header/cookie/live-data output needs \`render: 'server'\`.
|
|
321
|
+
- Static Request Demos should target concrete non-query URLs.
|
|
322
|
+
- After adding a \`js demo\`, verify the parent Page and Standalone Demo URL \`/page/_demo/demoName/\`.
|
|
323
|
+
- If \`rocket init\` fails because package.json has \`"type": "commonjs"\`, change it to \`"type": "module"\` or rerun \`npx rocket init --yes\`.
|
|
324
|
+
- If dev server watchers fail with \`EMFILE\`, run \`npm start -- --no-watch --no-open\` and use \`Ctrl+R\` for manual restarts.
|
|
325
|
+
- When smoke-testing Pages with curl, send \`Accept: text/html\`: \`curl -H 'Accept: text/html' http://localhost:8888/path\`.
|
|
326
|
+
- Keep \`npm run build\` passing; record Rocket package issues separately from local workarounds.
|
|
58
327
|
`;
|
|
59
328
|
|
|
60
329
|
export class RocketInit {
|
|
@@ -64,18 +333,20 @@ export class RocketInit {
|
|
|
64
333
|
async setupCommand(program) {
|
|
65
334
|
program
|
|
66
335
|
.command('init')
|
|
67
|
-
.description('create a
|
|
68
|
-
.
|
|
69
|
-
|
|
336
|
+
.description('create a Rocket docs starter')
|
|
337
|
+
.option('-y, --yes', 'update an explicit CommonJS package.json type to module')
|
|
338
|
+
.action(options => {
|
|
339
|
+
const result = this.init({ yes: options.yes });
|
|
70
340
|
reportInitResult(result);
|
|
71
341
|
});
|
|
72
342
|
}
|
|
73
343
|
|
|
74
344
|
/**
|
|
345
|
+
* @param {RocketInitOptions} [options]
|
|
75
346
|
* @returns {RocketInitResult}
|
|
76
347
|
*/
|
|
77
|
-
init() {
|
|
78
|
-
const packageJsonUpdate = preparePackageJsonUpdate('package.json');
|
|
348
|
+
init(options = {}) {
|
|
349
|
+
const packageJsonUpdate = preparePackageJsonUpdate('package.json', options);
|
|
79
350
|
/** @type {RocketInitResult} */
|
|
80
351
|
const result = {
|
|
81
352
|
created: [],
|
|
@@ -89,14 +360,27 @@ export class RocketInit {
|
|
|
89
360
|
writeJsonFile('package.json', packageJsonUpdate.packageJson);
|
|
90
361
|
}
|
|
91
362
|
|
|
363
|
+
const shouldCreateStarterPages =
|
|
364
|
+
existingRocketPageFileCount(process.cwd()) < EXISTING_ROCKET_PAGE_NOISE_THRESHOLD;
|
|
365
|
+
|
|
92
366
|
writeFileIfMissing(ROCKET_CONFIG_PATH, rocketConfigSource, result);
|
|
93
|
-
|
|
367
|
+
if (shouldCreateStarterPages) {
|
|
368
|
+
for (const filePath of STARTER_PAGE_FILES) {
|
|
369
|
+
writeFileIfMissing(filePath, STARTER_PAGE_SOURCES.get(filePath) || '', result);
|
|
370
|
+
}
|
|
371
|
+
}
|
|
94
372
|
writeFileIfMissing(ROCKET_AGENT_SKILL_PATH, rocketAgentSkillSource, result);
|
|
95
373
|
|
|
96
374
|
return result;
|
|
97
375
|
}
|
|
98
376
|
}
|
|
99
377
|
|
|
378
|
+
/**
|
|
379
|
+
* @typedef {{
|
|
380
|
+
* yes?: boolean;
|
|
381
|
+
* }} RocketInitOptions
|
|
382
|
+
*/
|
|
383
|
+
|
|
100
384
|
/**
|
|
101
385
|
* @typedef {{
|
|
102
386
|
* created: string[];
|
|
@@ -113,6 +397,7 @@ export class RocketInit {
|
|
|
113
397
|
|
|
114
398
|
/**
|
|
115
399
|
* @param {string} filePath
|
|
400
|
+
* @param {RocketInitOptions} options
|
|
116
401
|
* @returns {{
|
|
117
402
|
* changed: boolean;
|
|
118
403
|
* packageJson: Record<string, any>;
|
|
@@ -121,7 +406,7 @@ export class RocketInit {
|
|
|
121
406
|
* nextSteps: string[];
|
|
122
407
|
* } | undefined}
|
|
123
408
|
*/
|
|
124
|
-
function preparePackageJsonUpdate(filePath) {
|
|
409
|
+
function preparePackageJsonUpdate(filePath, options) {
|
|
125
410
|
if (!existsSync(filePath)) {
|
|
126
411
|
return undefined;
|
|
127
412
|
}
|
|
@@ -133,19 +418,19 @@ function preparePackageJsonUpdate(filePath) {
|
|
|
133
418
|
if (!isPlainRecord(packageJson)) {
|
|
134
419
|
throw new Error('package.json must contain a JSON object');
|
|
135
420
|
}
|
|
136
|
-
if (packageJson.type === 'commonjs') {
|
|
137
|
-
throw new Error(
|
|
138
|
-
`Rocket init expects an ESM project. Found package.json type "commonjs". ` +
|
|
139
|
-
`Change package.json to "type": "module" before running rocket init.`,
|
|
140
|
-
);
|
|
141
|
-
}
|
|
142
421
|
|
|
143
422
|
/** @type {string[]} */
|
|
144
423
|
const updated = [];
|
|
145
424
|
/** @type {string[]} */
|
|
146
425
|
const skipped = [];
|
|
147
426
|
|
|
148
|
-
if (
|
|
427
|
+
if (packageJson.type === 'commonjs') {
|
|
428
|
+
if (!options.yes) {
|
|
429
|
+
throw commonJsProjectError();
|
|
430
|
+
}
|
|
431
|
+
packageJson.type = 'module';
|
|
432
|
+
updated.push('type');
|
|
433
|
+
} else if (!hasOwn(packageJson, 'type')) {
|
|
149
434
|
packageJson.type = 'module';
|
|
150
435
|
updated.push('type');
|
|
151
436
|
} else if (packageJson.type === 'module') {
|
|
@@ -188,6 +473,19 @@ function preparePackageJsonUpdate(filePath) {
|
|
|
188
473
|
};
|
|
189
474
|
}
|
|
190
475
|
|
|
476
|
+
function commonJsProjectError() {
|
|
477
|
+
return new Error(
|
|
478
|
+
`Rocket init expects an ESM project. Found package.json type "commonjs".\n\n` +
|
|
479
|
+
`Update package.json before running rocket init:\n` +
|
|
480
|
+
`- "type": "commonjs",\n` +
|
|
481
|
+
`+ "type": "module",\n\n` +
|
|
482
|
+
`Then rerun:\n` +
|
|
483
|
+
` npx rocket init\n\n` +
|
|
484
|
+
`Or let Rocket apply that package.json change:\n` +
|
|
485
|
+
` npx rocket init --yes`,
|
|
486
|
+
);
|
|
487
|
+
}
|
|
488
|
+
|
|
191
489
|
/**
|
|
192
490
|
* @param {Record<string, any>} scripts
|
|
193
491
|
* @param {{
|
|
@@ -204,7 +502,7 @@ function addRocketScript(scripts, { genericName, fallbackName, command, updated,
|
|
|
204
502
|
updated.push(`scripts.${genericName}`);
|
|
205
503
|
return;
|
|
206
504
|
}
|
|
207
|
-
if (scripts[genericName]
|
|
505
|
+
if (scriptRunsRocketCommand(scripts[genericName], genericName)) {
|
|
208
506
|
skipped.push(`scripts.${genericName}`);
|
|
209
507
|
return;
|
|
210
508
|
}
|
|
@@ -237,8 +535,8 @@ function rocketNextSteps(packageJson) {
|
|
|
237
535
|
return ['npx rocket start', 'npx rocket build'];
|
|
238
536
|
}
|
|
239
537
|
return [
|
|
240
|
-
npmScriptCommand(packageJson.scripts, 'start', 'rocket:start'
|
|
241
|
-
npmScriptCommand(packageJson.scripts, 'build', 'rocket:build'
|
|
538
|
+
npmScriptCommand(packageJson.scripts, 'start', 'rocket:start'),
|
|
539
|
+
npmScriptCommand(packageJson.scripts, 'build', 'rocket:build'),
|
|
242
540
|
];
|
|
243
541
|
}
|
|
244
542
|
|
|
@@ -246,18 +544,85 @@ function rocketNextSteps(packageJson) {
|
|
|
246
544
|
* @param {Record<string, any>} scripts
|
|
247
545
|
* @param {string} genericName
|
|
248
546
|
* @param {string} fallbackName
|
|
249
|
-
* @param {string} command
|
|
250
547
|
*/
|
|
251
|
-
function npmScriptCommand(scripts, genericName, fallbackName
|
|
252
|
-
if (scripts[genericName]
|
|
548
|
+
function npmScriptCommand(scripts, genericName, fallbackName) {
|
|
549
|
+
if (scriptRunsRocketCommand(scripts[genericName], genericName)) {
|
|
253
550
|
return genericName === 'start' ? 'npm start' : `npm run ${genericName}`;
|
|
254
551
|
}
|
|
255
|
-
if (scripts[fallbackName]
|
|
552
|
+
if (scriptRunsRocketCommand(scripts[fallbackName], genericName)) {
|
|
256
553
|
return `npm run ${fallbackName}`;
|
|
257
554
|
}
|
|
258
555
|
return `npx rocket ${genericName}`;
|
|
259
556
|
}
|
|
260
557
|
|
|
558
|
+
/**
|
|
559
|
+
* @param {unknown} script
|
|
560
|
+
* @param {string} commandName
|
|
561
|
+
* @returns {boolean}
|
|
562
|
+
*/
|
|
563
|
+
function scriptRunsRocketCommand(script, commandName) {
|
|
564
|
+
if (typeof script !== 'string') {
|
|
565
|
+
return false;
|
|
566
|
+
}
|
|
567
|
+
const shellBoundary = '[\\s"\\\'`;&|()]';
|
|
568
|
+
return new RegExp(
|
|
569
|
+
`(^|${shellBoundary})(?:npx\\s+)?rocket\\s+${commandName}(?=$|${shellBoundary})`,
|
|
570
|
+
).test(script);
|
|
571
|
+
}
|
|
572
|
+
|
|
573
|
+
/**
|
|
574
|
+
* @param {string} directory
|
|
575
|
+
* @returns {number}
|
|
576
|
+
*/
|
|
577
|
+
function existingRocketPageFileCount(directory) {
|
|
578
|
+
if (!existsSync(directory)) {
|
|
579
|
+
return 0;
|
|
580
|
+
}
|
|
581
|
+
return countRocketPageFiles(directory, { count: 0 });
|
|
582
|
+
}
|
|
583
|
+
|
|
584
|
+
/**
|
|
585
|
+
* @param {string} directory
|
|
586
|
+
* @param {{ count: number }} state
|
|
587
|
+
* @returns {number}
|
|
588
|
+
*/
|
|
589
|
+
function countRocketPageFiles(directory, state) {
|
|
590
|
+
if (state.count >= EXISTING_ROCKET_PAGE_NOISE_THRESHOLD) {
|
|
591
|
+
return state.count;
|
|
592
|
+
}
|
|
593
|
+
for (const entry of readdirSync(directory, { withFileTypes: true })) {
|
|
594
|
+
if (shouldIgnoreRocketPageScanEntry(entry.name)) {
|
|
595
|
+
continue;
|
|
596
|
+
}
|
|
597
|
+
const entryPath = path.join(directory, entry.name);
|
|
598
|
+
if (entry.isDirectory()) {
|
|
599
|
+
countRocketPageFiles(entryPath, state);
|
|
600
|
+
} else if (isRocketPageFile(entry.name)) {
|
|
601
|
+
state.count += 1;
|
|
602
|
+
}
|
|
603
|
+
if (state.count >= EXISTING_ROCKET_PAGE_NOISE_THRESHOLD) {
|
|
604
|
+
return state.count;
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
return state.count;
|
|
608
|
+
}
|
|
609
|
+
|
|
610
|
+
/**
|
|
611
|
+
* @param {string} name
|
|
612
|
+
* @returns {boolean}
|
|
613
|
+
*/
|
|
614
|
+
function shouldIgnoreRocketPageScanEntry(name) {
|
|
615
|
+
return ['.git', '.netlify', 'coverage', 'dist', 'node_modules', 'temp'].includes(name);
|
|
616
|
+
}
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* @param {string} fileName
|
|
620
|
+
* @returns {boolean}
|
|
621
|
+
*/
|
|
622
|
+
function isRocketPageFile(fileName) {
|
|
623
|
+
return fileName.endsWith('.rocket.md') || fileName.endsWith('.rocket.js');
|
|
624
|
+
}
|
|
625
|
+
|
|
261
626
|
/**
|
|
262
627
|
* @param {string} filePath
|
|
263
628
|
* @param {string} contents
|