@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/RocketStart.js
CHANGED
|
@@ -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
|
|
16
|
-
|
|
17
|
-
|
|
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
|
-
|
|
30
|
+
/**
|
|
31
|
+
* @param {RocketStartOptions} [options]
|
|
32
|
+
*/
|
|
33
|
+
spawnServer(options = {}) {
|
|
22
34
|
process.stdin.setRawMode?.(true);
|
|
23
35
|
return spawn(
|
|
24
36
|
'node',
|
|
25
|
-
|
|
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
|
-
|
|
41
|
-
|
|
55
|
+
/**
|
|
56
|
+
* @param {RocketStartOptions} [options]
|
|
57
|
+
*/
|
|
58
|
+
async start(options = {}) {
|
|
59
|
+
this.startOptions = options;
|
|
60
|
+
this.server = this.spawnServer(options);
|
|
42
61
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
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
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
|
|
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
|
|
|
@@ -132,6 +119,13 @@ export function renderHeaderNav(links = []) {
|
|
|
132
119
|
`;
|
|
133
120
|
}
|
|
134
121
|
|
|
122
|
+
/**
|
|
123
|
+
* @param {string[] | undefined} stylesheets
|
|
124
|
+
*/
|
|
125
|
+
export function renderStylesheets(stylesheets = []) {
|
|
126
|
+
return stylesheets.map(href => html`<link rel="stylesheet" href=${href} />`);
|
|
127
|
+
}
|
|
128
|
+
|
|
135
129
|
/**
|
|
136
130
|
* @param {import('@rocket/js/types.js').PageMetadataCustomValue | undefined} customValue
|
|
137
131
|
* @returns {import('@rocket/js/types.js').AtlasDocAsideTip | false | undefined}
|
|
@@ -282,6 +276,7 @@ export const atlasDocLayout = (pageData, data) => {
|
|
|
282
276
|
href="${resolve('@awesome.me/webawesome/dist/styles/webawesome.css', import.meta)}"
|
|
283
277
|
/>
|
|
284
278
|
<link rel="stylesheet" href="${resolve('@rocket/js/layouts/atlasDoc.css', import.meta)}" />
|
|
279
|
+
${renderStylesheets(data.stylesheets)}
|
|
285
280
|
`,
|
|
286
281
|
},
|
|
287
282
|
);
|
|
@@ -3,7 +3,13 @@ import { document } from '../layout-helper.js';
|
|
|
3
3
|
import { resolve } from '../../resolve.js';
|
|
4
4
|
import { webAwesomeComponents } from '@rocket/js/components/web-awesome.js';
|
|
5
5
|
import { addBootstrapIconLibrary } from '../layout.js';
|
|
6
|
-
import {
|
|
6
|
+
import {
|
|
7
|
+
renderHeaderLogo,
|
|
8
|
+
renderHeaderNav,
|
|
9
|
+
renderSocials,
|
|
10
|
+
renderStylesheets,
|
|
11
|
+
} from './atlasDocLayout.js';
|
|
12
|
+
import { rocketDemoComponents } from '../../components.js';
|
|
7
13
|
|
|
8
14
|
/** @type {import('@rocket/js/types.js').Components} */
|
|
9
15
|
export const atlasHeroComponents = {
|
|
@@ -27,21 +33,11 @@ export const atlasHeroComponents = {
|
|
|
27
33
|
className: 'FeatureList',
|
|
28
34
|
loading: 'server',
|
|
29
35
|
},
|
|
36
|
+
...rocketDemoComponents,
|
|
30
37
|
'rocket-code-block': {
|
|
31
|
-
|
|
32
|
-
className: 'RocketCodeBlock',
|
|
38
|
+
...rocketDemoComponents['rocket-code-block'],
|
|
33
39
|
loading: 'hydrate:onClientLoad',
|
|
34
40
|
},
|
|
35
|
-
'rocket-js-demo': {
|
|
36
|
-
file: './RocketJsDemo.js',
|
|
37
|
-
className: 'RocketJsDemo',
|
|
38
|
-
loading: 'client',
|
|
39
|
-
},
|
|
40
|
-
'rocket-request-demo': {
|
|
41
|
-
file: './RocketRequestDemo.js',
|
|
42
|
-
className: 'RocketRequestDemo',
|
|
43
|
-
loading: 'client',
|
|
44
|
-
},
|
|
45
41
|
...webAwesomeComponents,
|
|
46
42
|
};
|
|
47
43
|
|
|
@@ -340,6 +336,7 @@ export const atlasHeroLayout = (pageData, data) => {
|
|
|
340
336
|
href="${resolve('@awesome.me/webawesome/dist/styles/webawesome.css', import.meta)}"
|
|
341
337
|
/>
|
|
342
338
|
<link rel="stylesheet" href="${resolve('@rocket/js/layouts/atlasHero.css', import.meta)}" />
|
|
339
|
+
${renderStylesheets(data.stylesheets)}
|
|
343
340
|
`,
|
|
344
341
|
},
|
|
345
342
|
);
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { html } from 'lit';
|
|
2
2
|
import { document } from '../layout-helper.js';
|
|
3
3
|
import { resolve } from '../../resolve.js';
|
|
4
|
+
import { renderStylesheets } from './atlasDocLayout.js';
|
|
4
5
|
|
|
5
6
|
/** @type {import('@rocket/js/types.js').Components} */
|
|
6
7
|
export const atlasNotFoundComponents = {};
|
|
@@ -61,9 +62,10 @@ export const atlasNotFoundLayout = (pageData, data) => {
|
|
|
61
62
|
{
|
|
62
63
|
menu: false,
|
|
63
64
|
headerContent: html`<link
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
65
|
+
rel="stylesheet"
|
|
66
|
+
href="${resolve('@rocket/js/layouts/atlasNotFound.css', import.meta)}"
|
|
67
|
+
/>
|
|
68
|
+
${renderStylesheets(data.stylesheets)}`,
|
|
67
69
|
},
|
|
68
70
|
);
|
|
69
71
|
};
|
package/src/layouts/layout.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
import { html } from 'lit';
|
|
3
3
|
import { document } from './layout-helper.js';
|
|
4
4
|
import { resolve } from '../resolve.js';
|
|
5
|
-
import {
|
|
5
|
+
import { addBootstrapIconLibrary } from '../icons.js';
|
|
6
6
|
|
|
7
7
|
/** @type {import('@rocket/js/types.js').Layout<null>} */
|
|
8
8
|
export const layout = data => {
|
|
@@ -49,14 +49,4 @@ export const singleDemoLayout = data => {
|
|
|
49
49
|
});
|
|
50
50
|
};
|
|
51
51
|
|
|
52
|
-
|
|
53
|
-
* @param {import('@rocket/js/PageData.js').PageData} pageData
|
|
54
|
-
*/
|
|
55
|
-
export function addBootstrapIconLibrary(pageData) {
|
|
56
|
-
pageData.addIconLibraries(
|
|
57
|
-
{
|
|
58
|
-
bootstrap: iconsFromPackage('bootstrap-icons', 'icons/*.svg'),
|
|
59
|
-
},
|
|
60
|
-
{ defaultIconLibrary: 'bootstrap' },
|
|
61
|
-
);
|
|
62
|
-
}
|
|
52
|
+
export { addBootstrapIconLibrary };
|
package/src/main.js
CHANGED
|
@@ -6,7 +6,8 @@ import { MessageChannel } from 'node:worker_threads';
|
|
|
6
6
|
import { customElements } from '@lit-labs/ssr-dom-shim';
|
|
7
7
|
import { readConfig } from './config.js';
|
|
8
8
|
|
|
9
|
-
const configFilePath = process.argv
|
|
9
|
+
const configFilePath = process.argv[2] || undefined;
|
|
10
|
+
const startOptions = readStartOptions(process.argv[3]);
|
|
10
11
|
|
|
11
12
|
customElements.define = (name, ctor) => {
|
|
12
13
|
customElements.__definitions.set(name, {
|
|
@@ -37,12 +38,13 @@ let devServerConfig = {
|
|
|
37
38
|
urlLifecycle: config.urlLifecycle,
|
|
38
39
|
iconLibraries: config.iconLibraries,
|
|
39
40
|
defaultIconLibrary: config.defaultIconLibrary,
|
|
41
|
+
watch: startOptions.watch,
|
|
40
42
|
}),
|
|
41
43
|
],
|
|
42
|
-
open: true,
|
|
44
|
+
open: startOptions.open ?? true,
|
|
43
45
|
nodeResolve: { exportConditions: ['browser'] },
|
|
44
|
-
watch: true,
|
|
45
|
-
port: 8888,
|
|
46
|
+
watch: startOptions.watch ?? true,
|
|
47
|
+
port: startOptions.port ?? 8888,
|
|
46
48
|
};
|
|
47
49
|
|
|
48
50
|
devServerConfig = config.adjustDevServerConfig(devServerConfig);
|
|
@@ -53,3 +55,18 @@ const server = await startDevServer({
|
|
|
53
55
|
readCliArgs: false,
|
|
54
56
|
readFileConfig: false,
|
|
55
57
|
});
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* @param {string | undefined} value
|
|
61
|
+
* @returns {{ port?: number; open?: boolean; watch?: boolean }}
|
|
62
|
+
*/
|
|
63
|
+
function readStartOptions(value) {
|
|
64
|
+
if (!value) {
|
|
65
|
+
return {};
|
|
66
|
+
}
|
|
67
|
+
const parsed = JSON.parse(value);
|
|
68
|
+
if (!parsed || typeof parsed !== 'object' || Array.isArray(parsed)) {
|
|
69
|
+
return {};
|
|
70
|
+
}
|
|
71
|
+
return /** @type {{ port?: number; open?: boolean; watch?: boolean }} */ (parsed);
|
|
72
|
+
}
|
|
@@ -89,14 +89,16 @@ export function matchStandaloneDemoUrl(pathname, origin, pages) {
|
|
|
89
89
|
if (!isMarkdownPage(page) || !page.demoNames?.includes(standaloneDemo.demoName)) {
|
|
90
90
|
continue;
|
|
91
91
|
}
|
|
92
|
-
const
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
92
|
+
for (const parentPathname of parentPathnameCandidates(standaloneDemo.parentPathname)) {
|
|
93
|
+
const match = matchPagePath(parentPathname, origin, routePath);
|
|
94
|
+
if (match) {
|
|
95
|
+
return {
|
|
96
|
+
page,
|
|
97
|
+
routePath,
|
|
98
|
+
params: match.pathname.groups,
|
|
99
|
+
variant: { kind: 'standalone-demo', demoName: standaloneDemo.demoName },
|
|
100
|
+
};
|
|
101
|
+
}
|
|
100
102
|
}
|
|
101
103
|
}
|
|
102
104
|
return null;
|
|
@@ -126,6 +128,17 @@ function matchPagePath(pathname, origin, routePath) {
|
|
|
126
128
|
return pattern.exec(pathname, origin);
|
|
127
129
|
}
|
|
128
130
|
|
|
131
|
+
/**
|
|
132
|
+
* @param {string} parentPathname
|
|
133
|
+
*/
|
|
134
|
+
function parentPathnameCandidates(parentPathname) {
|
|
135
|
+
const documentPath = normalizeDocumentPath(parentPathname);
|
|
136
|
+
if (documentPath === parentPathname) {
|
|
137
|
+
return [parentPathname];
|
|
138
|
+
}
|
|
139
|
+
return [parentPathname, documentPath];
|
|
140
|
+
}
|
|
141
|
+
|
|
129
142
|
/**
|
|
130
143
|
* @param {Page} page
|
|
131
144
|
*/
|
package/src/transform.js
CHANGED
|
@@ -13,6 +13,7 @@ import { visit } from 'unist-util-visit';
|
|
|
13
13
|
import { init, parse as parseExports } from 'es-module-lexer';
|
|
14
14
|
import { headLinesToTree } from './menu.js';
|
|
15
15
|
import { parseRequestDemoMetadata } from './requestDemoMetadata.js';
|
|
16
|
+
import ts from 'typescript';
|
|
16
17
|
|
|
17
18
|
/** @type {import('rehype-autolink-headings').Options} */
|
|
18
19
|
const headingAnchorOptions = {
|
|
@@ -119,13 +120,14 @@ export function contentFn(data, layout) {
|
|
|
119
120
|
*/
|
|
120
121
|
async function makeJsFile(serverCode, clientCode, markdown, headlines) {
|
|
121
122
|
let litImport = "import { html } from 'lit'";
|
|
122
|
-
|
|
123
|
+
const normalizedServerCode = normalizeLayoutExportBindings(serverCode);
|
|
124
|
+
if (/import\s*?{(?:\n|.)*?html(\n|.)*}.*/m.test(normalizedServerCode)) {
|
|
123
125
|
litImport = '';
|
|
124
126
|
}
|
|
125
127
|
return `
|
|
126
128
|
import {render} from '@lit-labs/ssr';
|
|
127
129
|
${litImport}
|
|
128
|
-
${
|
|
130
|
+
${normalizedServerCode}
|
|
129
131
|
export function contentFn(data, defaultLayout) {
|
|
130
132
|
let renderLayout = defaultLayout;
|
|
131
133
|
if (typeof layout !== 'undefined') {
|
|
@@ -143,6 +145,91 @@ export function contentFn(data, defaultLayout) {
|
|
|
143
145
|
}`;
|
|
144
146
|
}
|
|
145
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Direct ESM re-exports do not create local bindings, but the generated Markdown module calls the
|
|
150
|
+
* selected layout from inside contentFn. Rewriting only direct `layout` re-exports keeps the public
|
|
151
|
+
* Page syntax working while preserving the generated function's local binding lookup.
|
|
152
|
+
*
|
|
153
|
+
* @param {string} serverCode
|
|
154
|
+
* @returns {string}
|
|
155
|
+
*/
|
|
156
|
+
function normalizeLayoutExportBindings(serverCode) {
|
|
157
|
+
if (typeof serverCode !== 'string') {
|
|
158
|
+
return '';
|
|
159
|
+
}
|
|
160
|
+
const sourceFile = ts.createSourceFile(
|
|
161
|
+
'page-server.js',
|
|
162
|
+
serverCode,
|
|
163
|
+
ts.ScriptTarget.Latest,
|
|
164
|
+
true,
|
|
165
|
+
ts.ScriptKind.JS,
|
|
166
|
+
);
|
|
167
|
+
/** @type {{start: number; end: number; text: string}[]} */
|
|
168
|
+
const replacements = [];
|
|
169
|
+
|
|
170
|
+
for (const statement of sourceFile.statements) {
|
|
171
|
+
if (
|
|
172
|
+
!ts.isExportDeclaration(statement) ||
|
|
173
|
+
!statement.moduleSpecifier ||
|
|
174
|
+
!statement.exportClause ||
|
|
175
|
+
!ts.isNamedExports(statement.exportClause)
|
|
176
|
+
) {
|
|
177
|
+
continue;
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
const layoutExport = statement.exportClause.elements.find(
|
|
181
|
+
element => element.name.text === 'layout',
|
|
182
|
+
);
|
|
183
|
+
if (!layoutExport) {
|
|
184
|
+
continue;
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const moduleSpecifier = statement.moduleSpecifier.getText(sourceFile);
|
|
188
|
+
const attributes = statement.attributes ? ` ${statement.attributes.getText(sourceFile)}` : '';
|
|
189
|
+
const importedName = (layoutExport.propertyName || layoutExport.name).getText(sourceFile);
|
|
190
|
+
const layoutImportSpecifier =
|
|
191
|
+
importedName === 'layout' ? 'layout' : `${importedName} as layout`;
|
|
192
|
+
const replacementLines = [
|
|
193
|
+
`import { ${layoutImportSpecifier} } from ${moduleSpecifier}${attributes};`,
|
|
194
|
+
'export { layout };',
|
|
195
|
+
];
|
|
196
|
+
|
|
197
|
+
const remainingExports = statement.exportClause.elements.filter(
|
|
198
|
+
element => element !== layoutExport,
|
|
199
|
+
);
|
|
200
|
+
if (remainingExports.length) {
|
|
201
|
+
const remainingSpecifiers = remainingExports.map(element => {
|
|
202
|
+
if (element.propertyName) {
|
|
203
|
+
return `${element.propertyName.getText(sourceFile)} as ${element.name.getText(sourceFile)}`;
|
|
204
|
+
}
|
|
205
|
+
return element.name.getText(sourceFile);
|
|
206
|
+
});
|
|
207
|
+
replacementLines.push(
|
|
208
|
+
`export { ${remainingSpecifiers.join(', ')} } from ${moduleSpecifier}${attributes};`,
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
replacements.push({
|
|
213
|
+
start: statement.getStart(sourceFile),
|
|
214
|
+
end: statement.end,
|
|
215
|
+
text: replacementLines.join('\n'),
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
if (!replacements.length) {
|
|
220
|
+
return serverCode;
|
|
221
|
+
}
|
|
222
|
+
|
|
223
|
+
let normalizedCode = serverCode;
|
|
224
|
+
for (const replacement of replacements.toReversed()) {
|
|
225
|
+
normalizedCode =
|
|
226
|
+
normalizedCode.slice(0, replacement.start) +
|
|
227
|
+
replacement.text +
|
|
228
|
+
normalizedCode.slice(replacement.end);
|
|
229
|
+
}
|
|
230
|
+
return normalizedCode;
|
|
231
|
+
}
|
|
232
|
+
|
|
146
233
|
function parseDemos() {
|
|
147
234
|
/** @type {{name: string, code: string}[]} */
|
|
148
235
|
const demos = [];
|
package/src/wds-plugin.js
CHANGED
|
@@ -24,12 +24,13 @@ let pageRegistry = new Map();
|
|
|
24
24
|
let modules = new Map();
|
|
25
25
|
|
|
26
26
|
/** @typedef {import('@rocket/js/types.js').UrlLifecycleConfig} UrlLifecycleConfig */
|
|
27
|
-
/** @typedef {{ urlLifecycle?: UrlLifecycleConfig; siteHeadMetadata?: import('@rocket/js/types.js').SiteHeadMetadataConfig; siteOrigin?: string; siteDiscoverability?: import('@rocket/js/types.js').SiteDiscoverabilityConfig; iconLibraries?: import('@rocket/js/types.js').IconLibrariesConfig; defaultIconLibrary?: string; captureSocialPreviewImage?: import('./socialPreviewImages.js').SocialPreviewCapture }} RocketDevServerPluginOptions */
|
|
27
|
+
/** @typedef {{ urlLifecycle?: UrlLifecycleConfig; siteHeadMetadata?: import('@rocket/js/types.js').SiteHeadMetadataConfig; siteOrigin?: string; siteDiscoverability?: import('@rocket/js/types.js').SiteDiscoverabilityConfig; iconLibraries?: import('@rocket/js/types.js').IconLibrariesConfig; defaultIconLibrary?: string; captureSocialPreviewImage?: import('./socialPreviewImages.js').SocialPreviewCapture; watch?: boolean }} RocketDevServerPluginOptions */
|
|
28
28
|
/** @typedef {RocketDevServerPluginOptions | UrlLifecycleConfig} RocketDevServerPluginInput */
|
|
29
29
|
|
|
30
30
|
/** @type {(include: string[], exclude: (string | RegExp)[], resolverPort: import('node:worker_threads').MessagePort, options?: RocketDevServerPluginInput) => import('@web/dev-server-core').Plugin} */
|
|
31
31
|
export default (include, exclude, resolverPort, options = {}) => {
|
|
32
32
|
const pluginOptions = normalizePluginOptions(options);
|
|
33
|
+
const watchEnabled = pluginOptions.watch !== false;
|
|
33
34
|
const iconAssetStore = createIconAssetStore();
|
|
34
35
|
/** @type {import('node:fs').FSWatcher[]} */
|
|
35
36
|
const watchers = [];
|
|
@@ -65,19 +66,23 @@ export default (include, exclude, resolverPort, options = {}) => {
|
|
|
65
66
|
} else {
|
|
66
67
|
modules.set(message.url, new Set([message.parent]));
|
|
67
68
|
}
|
|
68
|
-
|
|
69
|
+
if (watchEnabled) {
|
|
70
|
+
watchFileDirectory(message.url);
|
|
71
|
+
}
|
|
69
72
|
resolverPort.postMessage('ok');
|
|
70
73
|
});
|
|
71
74
|
pageRegistry = await getPages(process.cwd(), include, exclude);
|
|
72
75
|
validateDevelopmentPublicAssets(pluginOptions);
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
76
|
+
if (watchEnabled) {
|
|
77
|
+
pageRegistry.forEach(page => {
|
|
78
|
+
watchFileDirectory(page.file);
|
|
79
|
+
});
|
|
80
|
+
modules.forEach((_, url) => {
|
|
81
|
+
watchFileDirectory(url);
|
|
82
|
+
});
|
|
83
|
+
}
|
|
79
84
|
const publicDir = path.join(process.cwd(), PUBLIC_ASSETS_DIR);
|
|
80
|
-
if (fs.existsSync(publicDir)) {
|
|
85
|
+
if (watchEnabled && fs.existsSync(publicDir)) {
|
|
81
86
|
fileWatcher?.add(publicDir);
|
|
82
87
|
}
|
|
83
88
|
},
|