@rocket/js 0.1.2 → 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +8 -2
- package/dist-types/exports/layouts/atlasBlog.d.ts +2 -0
- package/dist-types/exports/layouts/atlasBlog.d.ts.map +1 -0
- package/dist-types/exports/types/rocket.d.ts +60 -2
- package/dist-types/exports/types/rocket.d.ts.map +1 -1
- package/dist-types/exports/types.d.ts +1 -1
- package/dist-types/exports/types.d.ts.map +1 -1
- package/dist-types/src/PageData.d.ts +3 -1
- package/dist-types/src/PageData.d.ts.map +1 -1
- package/dist-types/src/RocketJsDemo.d.ts.map +1 -1
- package/dist-types/src/SocialPreviewPlayground.d.ts +1 -0
- package/dist-types/src/SocialPreviewPlayground.d.ts.map +1 -1
- package/dist-types/src/asyncMessage.d.ts +5 -0
- package/dist-types/src/asyncMessage.d.ts.map +1 -1
- package/dist-types/src/cli/RocketBuild.d.ts.map +1 -1
- package/dist-types/src/cli/RocketCli.d.ts.map +1 -1
- package/dist-types/src/cli/RocketStart.d.ts +6 -1
- package/dist-types/src/cli/RocketStart.d.ts.map +1 -1
- package/dist-types/src/component-hydration.d.ts.map +1 -1
- package/dist-types/src/components/RocketDrawer.d.ts +8 -0
- package/dist-types/src/components/RocketDrawer.d.ts.map +1 -1
- package/dist-types/src/components/RocketSocialLink.d.ts.map +1 -1
- package/dist-types/src/config.d.ts.map +1 -1
- package/dist-types/src/feeds.d.ts +38 -0
- package/dist-types/src/feeds.d.ts.map +1 -0
- package/dist-types/src/hydration/hydrationLoader.d.ts +13 -4
- package/dist-types/src/hydration/hydrationLoader.d.ts.map +1 -1
- package/dist-types/src/icons.d.ts.map +1 -1
- package/dist-types/src/layouts/atlas/atlasBlogLayout.d.ts +42 -0
- package/dist-types/src/layouts/atlas/atlasBlogLayout.d.ts.map +1 -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-helper.d.ts +3 -3
- package/dist-types/src/layouts/layout-helper.d.ts.map +1 -1
- package/dist-types/src/markdownHook.d.ts.map +1 -1
- package/dist-types/src/menu.d.ts +8 -3
- package/dist-types/src/menu.d.ts.map +1 -1
- package/dist-types/src/menus/MainMenu.d.ts.map +1 -1
- package/dist-types/src/{development-page-module-loader.d.ts → page-module-loader.d.ts} +7 -2
- package/dist-types/src/page-module-loader.d.ts.map +1 -0
- package/dist-types/src/page-runtime.d.ts +11 -0
- package/dist-types/src/page-runtime.d.ts.map +1 -1
- package/dist-types/src/pages.d.ts.map +1 -1
- package/dist-types/src/route-pattern.d.ts +10 -0
- package/dist-types/src/route-pattern.d.ts.map +1 -0
- package/dist-types/src/siteDiscoverability.d.ts +4 -0
- package/dist-types/src/siteDiscoverability.d.ts.map +1 -1
- package/dist-types/src/socialPreviewImages.d.ts +2 -1
- package/dist-types/src/socialPreviewImages.d.ts.map +1 -1
- package/dist-types/src/static-params.d.ts +33 -0
- package/dist-types/src/static-params.d.ts.map +1 -0
- package/dist-types/src/transform.d.ts +4 -0
- package/dist-types/src/transform.d.ts.map +1 -1
- package/dist-types/src/wds-plugin.d.ts.map +1 -1
- package/exports/layouts/atlasBlog.js +11 -0
- package/exports/types/rocket.test.ts +26 -0
- package/exports/types/rocket.ts +70 -2
- package/exports/types.ts +7 -0
- package/package.json +15 -31
- package/src/PageData.js +12 -2
- package/src/RocketJsDemo.js +1 -0
- package/src/SocialPreviewPlayground.js +26 -20
- package/src/asyncMessage.js +8 -2
- package/src/cli/RocketBuild.js +110 -13
- package/src/cli/RocketCli.js +11 -1
- package/src/cli/RocketStart.js +60 -16
- package/src/cli/cli.js +10 -1
- package/src/component-hydration.js +23 -3
- package/src/components/RocketDrawer.js +44 -10
- package/src/components/RocketSocialLink.js +26 -2
- package/src/config.js +33 -4
- package/src/feeds.js +212 -0
- package/src/hydration/hydrationLoader.js +80 -48
- package/src/icons.js +5 -0
- package/src/layouts/atlas/atlasBlog.css +143 -0
- package/src/layouts/atlas/atlasBlogLayout.js +234 -0
- package/src/layouts/atlas/atlasDocLayout.js +3 -2
- package/src/layouts/atlas/atlasHeroLayout.js +4 -6
- package/src/layouts/atlas/atlasNotFoundLayout.js +9 -21
- package/src/layouts/layout-helper.js +5 -5
- package/src/layouts/layout.js +2 -2
- package/src/loaded-page-module.js +3 -1
- package/src/markdownCompiler.js +2 -2
- package/src/markdownHook.js +7 -2
- package/src/menu.js +25 -19
- package/src/menus/MainMenu.js +10 -4
- package/src/{static-page-module-loader.js → page-module-loader.js} +6 -1
- package/src/page-pagination.js +2 -2
- package/src/page-runtime.js +48 -3
- package/src/pages.js +47 -56
- package/src/publicAssets.js +2 -2
- package/src/route-pattern.js +21 -0
- package/src/siteDiscoverability.js +14 -1
- package/src/socialPreviewImages.js +80 -14
- package/src/standalone-demo-url.js +2 -2
- package/src/static-params.js +114 -0
- package/src/transform.js +89 -22
- package/src/wds-plugin.js +16 -3
- package/dist-types/src/development-page-module-loader.d.ts.map +0 -1
- package/dist-types/src/static-page-module-loader.d.ts +0 -15
- package/dist-types/src/static-page-module-loader.d.ts.map +0 -1
- package/src/development-page-module-loader.js +0 -29
package/src/transform.js
CHANGED
|
@@ -90,10 +90,7 @@ export async function mdToJsSingleDemo(md, demo) {
|
|
|
90
90
|
|
|
91
91
|
const code = /** @type {{client: string; server: string}} */ (result.data.code);
|
|
92
92
|
|
|
93
|
-
|
|
94
|
-
if (/import\s*?{(?:\n|.)*?html(\n|.)*}.*/m.test(code.server)) {
|
|
95
|
-
litImport = '';
|
|
96
|
-
}
|
|
93
|
+
const litImport = hasTopLevelHtmlBinding(code.server) ? '' : "import { html } from 'lit'";
|
|
97
94
|
const moduleCode = `
|
|
98
95
|
import {render} from '@lit-labs/ssr';
|
|
99
96
|
${litImport}
|
|
@@ -119,11 +116,10 @@ export function contentFn(data, layout) {
|
|
|
119
116
|
* @returns {Promise<string>}
|
|
120
117
|
*/
|
|
121
118
|
async function makeJsFile(serverCode, clientCode, markdown, headlines) {
|
|
122
|
-
let litImport = "import { html } from 'lit'";
|
|
123
119
|
const normalizedServerCode = normalizeLayoutExportBindings(serverCode);
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
120
|
+
const litImport = hasTopLevelHtmlBinding(normalizedServerCode)
|
|
121
|
+
? ''
|
|
122
|
+
: "import { html } from 'lit'";
|
|
127
123
|
return `
|
|
128
124
|
import {render} from '@lit-labs/ssr';
|
|
129
125
|
${litImport}
|
|
@@ -145,6 +141,56 @@ export function contentFn(data, defaultLayout) {
|
|
|
145
141
|
}`;
|
|
146
142
|
}
|
|
147
143
|
|
|
144
|
+
/**
|
|
145
|
+
* The generated Markdown module wraps the page content in a lit `html` template, so it must inject
|
|
146
|
+
* `import { html } from 'lit'` unless the Page's server code already provides a top-level `html`
|
|
147
|
+
* binding (an import binding or a top-level declaration).
|
|
148
|
+
*
|
|
149
|
+
* @param {string} code
|
|
150
|
+
* @returns {boolean}
|
|
151
|
+
*/
|
|
152
|
+
function hasTopLevelHtmlBinding(code) {
|
|
153
|
+
if (typeof code !== 'string' || code === '') {
|
|
154
|
+
return false;
|
|
155
|
+
}
|
|
156
|
+
const sourceFile = ts.createSourceFile(
|
|
157
|
+
'page-server.js',
|
|
158
|
+
code,
|
|
159
|
+
ts.ScriptTarget.Latest,
|
|
160
|
+
true,
|
|
161
|
+
ts.ScriptKind.JS,
|
|
162
|
+
);
|
|
163
|
+
for (const statement of sourceFile.statements) {
|
|
164
|
+
if (ts.isImportDeclaration(statement) && statement.importClause) {
|
|
165
|
+
const { name, namedBindings } = statement.importClause;
|
|
166
|
+
if (name?.text === 'html') {
|
|
167
|
+
return true;
|
|
168
|
+
}
|
|
169
|
+
if (namedBindings) {
|
|
170
|
+
if (ts.isNamespaceImport(namedBindings)) {
|
|
171
|
+
if (namedBindings.name.text === 'html') {
|
|
172
|
+
return true;
|
|
173
|
+
}
|
|
174
|
+
} else if (namedBindings.elements.some(element => element.name.text === 'html')) {
|
|
175
|
+
return true;
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
} else if (ts.isVariableStatement(statement)) {
|
|
179
|
+
for (const declaration of statement.declarationList.declarations) {
|
|
180
|
+
if (ts.isIdentifier(declaration.name) && declaration.name.text === 'html') {
|
|
181
|
+
return true;
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
} else if (
|
|
185
|
+
(ts.isFunctionDeclaration(statement) || ts.isClassDeclaration(statement)) &&
|
|
186
|
+
statement.name?.text === 'html'
|
|
187
|
+
) {
|
|
188
|
+
return true;
|
|
189
|
+
}
|
|
190
|
+
}
|
|
191
|
+
return false;
|
|
192
|
+
}
|
|
193
|
+
|
|
148
194
|
/**
|
|
149
195
|
* Direct ESM re-exports do not create local bindings, but the generated Markdown module calls the
|
|
150
196
|
* selected layout from inside contentFn. Rewriting only direct `layout` re-exports keeps the public
|
|
@@ -242,8 +288,7 @@ function parseDemos() {
|
|
|
242
288
|
codePreview.visited = true;
|
|
243
289
|
codePreview.meta = demoCodeBlockMeta(node.meta);
|
|
244
290
|
const code = node.value;
|
|
245
|
-
const
|
|
246
|
-
const name = parsed[1][0].ln || parsed[1][0].n;
|
|
291
|
+
const name = demoExportName(code);
|
|
247
292
|
// we transform a code node into a root node, so cast
|
|
248
293
|
const rootNode = /** @type {import('mdast').Root} */ (/** @type {unknown} */ (node));
|
|
249
294
|
rootNode.type = 'root';
|
|
@@ -357,10 +402,26 @@ function isRequestDemoCodeNode(node) {
|
|
|
357
402
|
/**
|
|
358
403
|
* @param {import('mdast').Code} node
|
|
359
404
|
*/
|
|
360
|
-
function isDemoCodeNode(node) {
|
|
405
|
+
export function isDemoCodeNode(node) {
|
|
361
406
|
return node.lang === 'js' && /(?:^|\s)demo(?:\s|$)/.test(node.meta || '');
|
|
362
407
|
}
|
|
363
408
|
|
|
409
|
+
/**
|
|
410
|
+
* @param {string} code
|
|
411
|
+
* @returns {string}
|
|
412
|
+
*/
|
|
413
|
+
function demoExportName(code) {
|
|
414
|
+
const [, moduleExports] = parseExports(code);
|
|
415
|
+
const name = moduleExports[0] && (moduleExports[0].ln || moduleExports[0].n);
|
|
416
|
+
if (!name) {
|
|
417
|
+
throw new Error(
|
|
418
|
+
'A ```js demo code block must export the demo function, ' +
|
|
419
|
+
'e.g. "export const myDemo = () => html`...`".',
|
|
420
|
+
);
|
|
421
|
+
}
|
|
422
|
+
return name;
|
|
423
|
+
}
|
|
424
|
+
|
|
364
425
|
/**
|
|
365
426
|
* @param {string | null | undefined} meta
|
|
366
427
|
*/
|
|
@@ -400,8 +461,7 @@ function extractSingleDemo(demoName) {
|
|
|
400
461
|
codePreview.visited = true;
|
|
401
462
|
codePreview.meta = demoCodeBlockMeta(node.meta);
|
|
402
463
|
const code = node.value;
|
|
403
|
-
const
|
|
404
|
-
const name = parsed[1][0].ln || parsed[1][0].n;
|
|
464
|
+
const name = demoExportName(code);
|
|
405
465
|
if (name !== demoName) {
|
|
406
466
|
return;
|
|
407
467
|
}
|
|
@@ -596,17 +656,24 @@ function readStringProperty(value) {
|
|
|
596
656
|
*/
|
|
597
657
|
function escapeOutsideTemplates(string) {
|
|
598
658
|
let result = '';
|
|
599
|
-
let
|
|
659
|
+
let braceDepth = 0;
|
|
600
660
|
for (let i = 0; i < string.length; i++) {
|
|
601
|
-
if (
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
} else {
|
|
661
|
+
if (braceDepth === 0) {
|
|
662
|
+
if (string[i] === '$' && string[i + 1] === '{' && string[i - 1] !== '\\') {
|
|
663
|
+
braceDepth = 1;
|
|
664
|
+
result += '${';
|
|
665
|
+
i++;
|
|
666
|
+
continue;
|
|
667
|
+
}
|
|
609
668
|
result += string[i].replace('\\', '\\\\').replace('`', '\\`');
|
|
669
|
+
} else {
|
|
670
|
+
// Track nested braces so expressions like `${ fn({ a: 1 }) }` stay intact.
|
|
671
|
+
if (string[i] === '{') {
|
|
672
|
+
braceDepth++;
|
|
673
|
+
} else if (string[i] === '}') {
|
|
674
|
+
braceDepth--;
|
|
675
|
+
}
|
|
676
|
+
result += string[i];
|
|
610
677
|
}
|
|
611
678
|
}
|
|
612
679
|
|
package/src/wds-plugin.js
CHANGED
|
@@ -6,7 +6,8 @@ import fs from 'node:fs';
|
|
|
6
6
|
import { debounce } from './debounce.js';
|
|
7
7
|
import path from 'node:path';
|
|
8
8
|
import { createIconAssetStore, rocketIconRuntimeOutputs } from './icons.js';
|
|
9
|
-
import {
|
|
9
|
+
import { createPageModuleLoader } from './page-module-loader.js';
|
|
10
|
+
import { matchPageFeedPath } from './feeds.js';
|
|
10
11
|
import { PageRuntime, PageRuntimeError } from './page-runtime.js';
|
|
11
12
|
import {
|
|
12
13
|
PUBLIC_ASSETS_DIR,
|
|
@@ -23,6 +24,9 @@ let pageRegistry = new Map();
|
|
|
23
24
|
/** @type {Map<string, Set<string>>} */
|
|
24
25
|
let modules = new Map();
|
|
25
26
|
|
|
27
|
+
/** @type {import('./publicAssets.js').PublicAsset[] | null} */
|
|
28
|
+
let cachedPublicAssets = null;
|
|
29
|
+
|
|
26
30
|
/** @typedef {import('@rocket/js/types.js').UrlLifecycleConfig} UrlLifecycleConfig */
|
|
27
31
|
/** @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
32
|
/** @typedef {RocketDevServerPluginOptions | UrlLifecycleConfig} RocketDevServerPluginInput */
|
|
@@ -84,6 +88,10 @@ export default (include, exclude, resolverPort, options = {}) => {
|
|
|
84
88
|
const publicDir = path.join(process.cwd(), PUBLIC_ASSETS_DIR);
|
|
85
89
|
if (watchEnabled && fs.existsSync(publicDir)) {
|
|
86
90
|
fileWatcher?.add(publicDir);
|
|
91
|
+
// re-discover and re-validate Public Assets on the next request
|
|
92
|
+
fileWatcher?.on?.('all', () => {
|
|
93
|
+
cachedPublicAssets = null;
|
|
94
|
+
});
|
|
87
95
|
}
|
|
88
96
|
},
|
|
89
97
|
serverStop() {
|
|
@@ -128,7 +136,7 @@ export default (include, exclude, resolverPort, options = {}) => {
|
|
|
128
136
|
type: 'image/svg+xml',
|
|
129
137
|
};
|
|
130
138
|
}
|
|
131
|
-
const publicAssets = validateDevelopmentPublicAssets(pluginOptions);
|
|
139
|
+
const publicAssets = cachedPublicAssets ?? validateDevelopmentPublicAssets(pluginOptions);
|
|
132
140
|
const publicAsset = findPublicAsset(publicAssets, context.path);
|
|
133
141
|
if (publicAsset) {
|
|
134
142
|
return /** @type {any} */ ({
|
|
@@ -151,6 +159,10 @@ export default (include, exclude, resolverPort, options = {}) => {
|
|
|
151
159
|
} else if (context.header.accept === '*/*') {
|
|
152
160
|
isPage = false;
|
|
153
161
|
}
|
|
162
|
+
// Feed readers fetch Page Feeds without document Accept headers.
|
|
163
|
+
if (!isPage && matchPageFeedPath(context.path, pageRegistry)) {
|
|
164
|
+
isPage = true;
|
|
165
|
+
}
|
|
154
166
|
if (!isPage) {
|
|
155
167
|
const pathname = new URLPattern().exec(context.header.referer)?.pathname.input;
|
|
156
168
|
if (!pathname) {
|
|
@@ -196,6 +208,7 @@ function validateDevelopmentPublicAssets(pluginOptions) {
|
|
|
196
208
|
}),
|
|
197
209
|
emitRedirectFallbacks: false,
|
|
198
210
|
});
|
|
211
|
+
cachedPublicAssets = publicAssets;
|
|
199
212
|
return publicAssets;
|
|
200
213
|
}
|
|
201
214
|
|
|
@@ -222,7 +235,7 @@ async function renderPageRuntime(
|
|
|
222
235
|
) {
|
|
223
236
|
const pageRuntime = new PageRuntime({
|
|
224
237
|
pages: pageRegistry,
|
|
225
|
-
pageModuleLoader:
|
|
238
|
+
pageModuleLoader: createPageModuleLoader(),
|
|
226
239
|
urlLifecycle,
|
|
227
240
|
siteHeadMetadata,
|
|
228
241
|
siteOrigin,
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"development-page-module-loader.d.ts","sourceRoot":"","sources":["../../src/development-page-module-loader.js"],"names":[],"mappings":"AAGA,6FAA6F;AAE7F;IAEI;;OAEG;4BADQ,uBAAuB;;;;;;;EAoBrC;sCAzBa,OAAO,mBAAmB,EAAE,uBAAuB"}
|
|
@@ -1,15 +0,0 @@
|
|
|
1
|
-
/** @typedef {import('./page-runtime.js').PageModuleLoaderOptions} PageModuleLoaderOptions */
|
|
2
|
-
export function createStaticPageModuleLoader(): {
|
|
3
|
-
/**
|
|
4
|
-
* @param {PageModuleLoaderOptions} options
|
|
5
|
-
*/
|
|
6
|
-
load({ page, variant }: PageModuleLoaderOptions): Promise<{
|
|
7
|
-
kind: "markdown";
|
|
8
|
-
contentFn: unknown;
|
|
9
|
-
} | {
|
|
10
|
-
kind: "javascript";
|
|
11
|
-
body: unknown;
|
|
12
|
-
}>;
|
|
13
|
-
};
|
|
14
|
-
export type PageModuleLoaderOptions = import("./page-runtime.js").PageModuleLoaderOptions;
|
|
15
|
-
//# sourceMappingURL=static-page-module-loader.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"static-page-module-loader.d.ts","sourceRoot":"","sources":["../../src/static-page-module-loader.js"],"names":[],"mappings":"AAGA,6FAA6F;AAE7F;IAEI;;OAEG;4BADQ,uBAAuB;;;;;;;EAoBrC;sCAzBa,OAAO,mBAAmB,EAAE,uBAAuB"}
|
|
@@ -1,29 +0,0 @@
|
|
|
1
|
-
import { normalizeLoadedPageModule } from './loaded-page-module.js';
|
|
2
|
-
import { parseComponents } from './components.js';
|
|
3
|
-
|
|
4
|
-
/** @typedef {import('./page-runtime.js').PageModuleLoaderOptions} PageModuleLoaderOptions */
|
|
5
|
-
|
|
6
|
-
export function createDevelopmentPageModuleLoader() {
|
|
7
|
-
return {
|
|
8
|
-
/**
|
|
9
|
-
* @param {PageModuleLoaderOptions} options
|
|
10
|
-
*/
|
|
11
|
-
async load({ page, variant }) {
|
|
12
|
-
if (!page.file.endsWith('.js')) {
|
|
13
|
-
const importAttributes = { type: 'rocketLoadMdInitial' };
|
|
14
|
-
if (typeof variant === 'object' && variant.kind === 'standalone-demo') {
|
|
15
|
-
Object.assign(importAttributes, { singleDemo: variant.demoName });
|
|
16
|
-
}
|
|
17
|
-
const module = await import(`./${page.file}`, {
|
|
18
|
-
with: importAttributes,
|
|
19
|
-
});
|
|
20
|
-
return normalizeLoadedPageModule({ kind: 'markdown', module, parseComponents });
|
|
21
|
-
}
|
|
22
|
-
|
|
23
|
-
const module = await import(`./${page.file}`, {
|
|
24
|
-
with: { type: 'rocketLoadJsInitial' },
|
|
25
|
-
});
|
|
26
|
-
return normalizeLoadedPageModule({ kind: 'javascript', module });
|
|
27
|
-
},
|
|
28
|
-
};
|
|
29
|
-
}
|