@rocket/js 0.2.0 → 0.3.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.
Files changed (69) hide show
  1. package/dist-types/exports/adapters/cloudflare.d.ts +2 -0
  2. package/dist-types/exports/adapters/cloudflare.d.ts.map +1 -0
  3. package/dist-types/exports/ssr.d.ts +1 -1
  4. package/dist-types/exports/types/rocket.d.ts +11 -0
  5. package/dist-types/exports/types/rocket.d.ts.map +1 -1
  6. package/dist-types/exports/types.d.ts +1 -1
  7. package/dist-types/exports/types.d.ts.map +1 -1
  8. package/dist-types/src/PageData.d.ts +9 -0
  9. package/dist-types/src/PageData.d.ts.map +1 -1
  10. package/dist-types/src/RocketCodeBlock.d.ts.map +1 -1
  11. package/dist-types/src/RocketJsDemo.d.ts.map +1 -1
  12. package/dist-types/src/adapters/cloudflare.d.ts +27 -0
  13. package/dist-types/src/adapters/cloudflare.d.ts.map +1 -0
  14. package/dist-types/src/adapters/netlify.d.ts.map +1 -1
  15. package/dist-types/src/adapters/redirects.d.ts +14 -0
  16. package/dist-types/src/adapters/redirects.d.ts.map +1 -0
  17. package/dist-types/src/icons.d.ts +27 -0
  18. package/dist-types/src/icons.d.ts.map +1 -1
  19. package/dist-types/src/layouts/atlas/atlasBlogLayout.d.ts.map +1 -1
  20. package/dist-types/src/layouts/atlas/atlasDocLayout.d.ts.map +1 -1
  21. package/dist-types/src/layouts/atlas/atlasHeroLayout.d.ts.map +1 -1
  22. package/dist-types/src/layouts/atlas/atlasNotFoundLayout.d.ts.map +1 -1
  23. package/dist-types/src/layouts/layout-helper.d.ts.map +1 -1
  24. package/dist-types/src/markdownHook.d.ts.map +1 -1
  25. package/dist-types/src/menus/MainMenu.d.ts.map +1 -1
  26. package/dist-types/src/menus/RocketMenu.d.ts.map +1 -1
  27. package/dist-types/src/menus/RocketToc.d.ts.map +1 -1
  28. package/dist-types/src/menus/pageNavigation.d.ts.map +1 -1
  29. package/dist-types/src/prefer-project-packages-plugin.d.ts +9 -0
  30. package/dist-types/src/prefer-project-packages-plugin.d.ts.map +1 -0
  31. package/dist-types/src/project-package-resolution.d.ts +8 -0
  32. package/dist-types/src/project-package-resolution.d.ts.map +1 -0
  33. package/dist-types/src/publicAssets.d.ts.map +1 -1
  34. package/dist-types/src/resolve.d.ts.map +1 -1
  35. package/dist-types/src/ssr.d.ts +3 -0
  36. package/dist-types/src/ssr.d.ts.map +1 -1
  37. package/dist-types/src/transform.d.ts.map +1 -1
  38. package/exports/adapters/cloudflare.js +1 -0
  39. package/exports/ssr.js +1 -1
  40. package/exports/types/rocket.ts +17 -4
  41. package/exports/types.ts +1 -0
  42. package/package.json +41 -26
  43. package/src/PageData.js +15 -0
  44. package/src/RocketCodeBlock.js +11 -9
  45. package/src/RocketJsDemo.js +8 -6
  46. package/src/adapters/cloudflare.js +522 -0
  47. package/src/adapters/netlify.js +2 -24
  48. package/src/adapters/redirects.js +31 -0
  49. package/src/cli/RocketBuild.js +5 -2
  50. package/src/config.js +1 -1
  51. package/src/icons.js +143 -1
  52. package/src/layouts/atlas/atlasBlogLayout.js +45 -31
  53. package/src/layouts/atlas/atlasDocLayout.js +22 -16
  54. package/src/layouts/atlas/atlasHeroLayout.js +39 -31
  55. package/src/layouts/atlas/atlasNotFoundLayout.js +5 -3
  56. package/src/layouts/layout-helper.js +38 -26
  57. package/src/main.js +17 -0
  58. package/src/markdownCompiler.js +5 -1
  59. package/src/menus/MainMenu.js +17 -13
  60. package/src/menus/RocketMenu.js +25 -19
  61. package/src/menus/RocketToc.js +7 -5
  62. package/src/menus/pageNavigation.js +24 -10
  63. package/src/prefer-project-packages-plugin.js +31 -0
  64. package/src/project-package-resolution.js +36 -0
  65. package/src/publicAssets.js +9 -2
  66. package/src/resolve.js +32 -2
  67. package/src/ssr.js +3 -0
  68. package/src/transform.js +15 -9
  69. package/src/wds-plugin.js +2 -1
@@ -47,25 +47,31 @@ export class RocketMenu extends LitElement {
47
47
 
48
48
  return html`
49
49
  <li class=${classMap(itemClasses)}>
50
- ${page.menuNoLink
51
- ? html`<span class=${classMap(classes)}>${title}</span>`
52
- : html`<a
53
- href=${ifDefined(page.url)}
54
- class=${classMap(classes)}
55
- aria-current=${ifDefined(isCurrent ? 'page' : undefined)}
56
- >${page.iconName
57
- ? html`<rocket-icon
58
- class="nav-icon"
59
- name=${page.iconName}
60
- aria-hidden="true"
61
- ></rocket-icon>`
62
- : ''}<span class="nav-text">${title}</span></a
63
- >`}
64
- ${page.children.length > 0
65
- ? html`<ul>
66
- ${page.children.map(child => this.list(child, depth + 1))}
67
- </ul>`
68
- : ''}
50
+ ${
51
+ page.menuNoLink
52
+ ? html`<span class=${classMap(classes)}>${title}</span>`
53
+ : html`<a
54
+ href=${ifDefined(page.url)}
55
+ class=${classMap(classes)}
56
+ aria-current=${ifDefined(isCurrent ? 'page' : undefined)}
57
+ >${
58
+ page.iconName
59
+ ? html`<rocket-icon
60
+ class="nav-icon"
61
+ name=${page.iconName}
62
+ aria-hidden="true"
63
+ ></rocket-icon>`
64
+ : ''
65
+ }<span class="nav-text">${title}</span></a
66
+ >`
67
+ }
68
+ ${
69
+ page.children.length > 0
70
+ ? html`<ul>
71
+ ${page.children.map(child => this.list(child, depth + 1))}
72
+ </ul>`
73
+ : ''
74
+ }
69
75
  </li>
70
76
  `;
71
77
  }
@@ -68,11 +68,13 @@ export class RocketToc extends LitElement {
68
68
  >
69
69
  ${headline.text}
70
70
  </a>
71
- ${headline.children.length > 0
72
- ? html`<ol>
73
- ${headline.children.map(child => this.list(child, depth + 1))}
74
- </ol>`
75
- : html``}
71
+ ${
72
+ headline.children.length > 0
73
+ ? html`<ol>
74
+ ${headline.children.map(child => this.list(child, depth + 1))}
75
+ </ol>`
76
+ : html``
77
+ }
76
78
  </li>
77
79
  `;
78
80
  }
@@ -50,20 +50,34 @@ export function renderPageNavigationCard(page, direction) {
50
50
  const isNext = direction === 'next';
51
51
  const label = isNext ? 'Next Page' : 'Previous Page';
52
52
  const icon = html`<span class="page-card-icon" aria-hidden="true">
53
- ${isNext
54
- ? html`<svg class="page-card-arrow" viewBox="0 0 36 16" focusable="false" aria-hidden="true">
55
- <path d="M5 8h25m-7-6 7 6-7 6" />
56
- </svg>`
57
- : html`<svg class="page-card-arrow" viewBox="0 0 36 16" focusable="false" aria-hidden="true">
58
- <path d="M31 8H6m7-6-7 6 7 6" />
59
- </svg>`}
53
+ ${
54
+ isNext
55
+ ? html`<svg
56
+ class="page-card-arrow"
57
+ viewBox="0 0 36 16"
58
+ focusable="false"
59
+ aria-hidden="true"
60
+ >
61
+ <path d="M5 8h25m-7-6 7 6-7 6" />
62
+ </svg>`
63
+ : html`<svg
64
+ class="page-card-arrow"
65
+ viewBox="0 0 36 16"
66
+ focusable="false"
67
+ aria-hidden="true"
68
+ >
69
+ <path d="M31 8H6m7-6-7 6 7 6" />
70
+ </svg>`
71
+ }
60
72
  </span>`;
61
73
  const text = html`<span class="page-card-text">
62
74
  <span class="page-card-label">${label}</span>
63
75
  <strong class="page-card-title">${page.linkText}</strong>
64
- ${page.sectionLabel
65
- ? html`<span class="page-card-section">${page.sectionLabel}</span>`
66
- : nothing}
76
+ ${
77
+ page.sectionLabel
78
+ ? html`<span class="page-card-section">${page.sectionLabel}</span>`
79
+ : nothing
80
+ }
67
81
  </span>`;
68
82
 
69
83
  return html`<a
@@ -0,0 +1,31 @@
1
+ import { resolveProjectBrowserPackage } from './project-package-resolution.js';
2
+ import { resolve } from './resolve.js';
3
+
4
+ /**
5
+ * Keeps browser packages shared between a Site Author project and a linked Rocket checkout.
6
+ * Packages absent from the project are left to normal Node resolution, which falls back to
7
+ * Rocket's dependency.
8
+ *
9
+ * @returns {import('@web/dev-server-core').Plugin}
10
+ */
11
+ export function preferProjectPackagesPlugin() {
12
+ /** @type {Map<string, string | undefined>} */
13
+ const resolvedImports = new Map();
14
+
15
+ return {
16
+ name: 'rocket-prefer-project-packages',
17
+ resolveImport({ source }) {
18
+ if (resolvedImports.has(source)) {
19
+ return resolvedImports.get(source);
20
+ }
21
+ let resolved;
22
+ try {
23
+ resolved = resolveProjectBrowserPackage(source) ? resolve(source, import.meta) : undefined;
24
+ } catch {
25
+ resolved = undefined;
26
+ }
27
+ resolvedImports.set(source, resolved);
28
+ return resolved;
29
+ },
30
+ };
31
+ }
@@ -0,0 +1,36 @@
1
+ import { moduleResolve } from 'import-meta-resolve';
2
+ import path from 'node:path';
3
+ import { pathToFileURL } from 'node:url';
4
+
5
+ const browserImportConditions = new Set(['browser', 'import', 'module']);
6
+
7
+ /**
8
+ * Resolve a bare package import from the Site Author project using browser ESM conditions.
9
+ * Built-in modules and packages absent from the project are left to another resolver.
10
+ *
11
+ * @param {string} specifier
12
+ */
13
+ export function resolveProjectBrowserPackage(specifier) {
14
+ if (!isPackageSpecifier(specifier)) {
15
+ return undefined;
16
+ }
17
+ try {
18
+ const projectParent = pathToFileURL(path.join(process.cwd(), 'package.json'));
19
+ const resolved = moduleResolve(specifier, projectParent, browserImportConditions, false);
20
+ return resolved.protocol === 'file:' ? resolved.href : undefined;
21
+ } catch {
22
+ return undefined;
23
+ }
24
+ }
25
+
26
+ /**
27
+ * @param {string} specifier
28
+ */
29
+ function isPackageSpecifier(specifier) {
30
+ return (
31
+ !specifier.startsWith('.') &&
32
+ !specifier.startsWith('/') &&
33
+ !specifier.startsWith('#') &&
34
+ !URL.canParse(specifier)
35
+ );
36
+ }
@@ -5,6 +5,12 @@ import { paginatedArchivePaths } from './page-pagination.js';
5
5
  import { normalizeDocumentPath, standaloneDemoPaths } from './standalone-demo-url.js';
6
6
  import { needsDefaultSocialPreviewImage, publicSocialPreviewPages } from './socialPreviewImages.js';
7
7
 
8
+ /** Adapters that write `_redirects` into the build output, by adapter name. */
9
+ const REDIRECTS_FILE_HOSTS = new Map([
10
+ ['netlify', 'Netlify'],
11
+ ['cloudflare', 'Cloudflare'],
12
+ ]);
13
+
8
14
  export const PUBLIC_ASSETS_DIR = 'public';
9
15
 
10
16
  /**
@@ -237,8 +243,9 @@ function generatedOutputClaims({ pages, generatedPages, config, emitRedirectFall
237
243
  if (config.siteDiscoverability?.robots === true) {
238
244
  claims.set('/robots.txt', 'generated Robots File');
239
245
  }
240
- if (config.adapter?.name === 'netlify' && config.urlLifecycle?.redirects?.length) {
241
- claims.set('/_redirects', 'Netlify Redirect output');
246
+ const redirectsFileHost = REDIRECTS_FILE_HOSTS.get(config.adapter?.name ?? '');
247
+ if (redirectsFileHost && config.urlLifecycle?.redirects?.length) {
248
+ claims.set('/_redirects', `${redirectsFileHost} Redirect output`);
242
249
  }
243
250
 
244
251
  return claims;
package/src/resolve.js CHANGED
@@ -1,5 +1,6 @@
1
1
  import path from 'path';
2
2
  import { fileURLToPath } from 'url';
3
+ import { resolveProjectBrowserPackage } from './project-package-resolution.js';
3
4
 
4
5
  /**
5
6
  * @param {string} url
@@ -9,7 +10,36 @@ import { fileURLToPath } from 'url';
9
10
  export function resolve(url, meta) {
10
11
  // get current directory
11
12
  const outputFilePath = process.cwd();
12
- const resolvedPath = meta.resolve(url);
13
+ const resolvedPath = resolveProjectBrowserPackage(url) ?? meta.resolve(url);
13
14
  const rel = path.relative(outputFilePath, fileURLToPath(resolvedPath));
14
- return (process.env.ENVIRONMENT === 'BUILD' ? '/../' : '/') + rel.split(path.sep).join('/');
15
+ if (process.env.ENVIRONMENT === 'BUILD') {
16
+ return `/../${toBrowserPath(rel)}`;
17
+ }
18
+ return developmentBrowserPath(rel);
19
+ }
20
+
21
+ /**
22
+ * Web Dev Server represents files outside its root with a path that records how many parent
23
+ * directories to traverse. Browsers normalize ordinary `/../` segments before making a request,
24
+ * so linked packages need this explicit form.
25
+ *
26
+ * @param {string} relativePath
27
+ */
28
+ function developmentBrowserPath(relativePath) {
29
+ const parts = relativePath.split(path.sep);
30
+ let outsideRootDepth = 0;
31
+ while (parts[outsideRootDepth] === '..') {
32
+ outsideRootDepth += 1;
33
+ }
34
+ const browserPath = parts.slice(outsideRootDepth).join('/');
35
+ return outsideRootDepth
36
+ ? `/__wds-outside-root__/${outsideRootDepth}/${browserPath}`
37
+ : `/${browserPath}`;
38
+ }
39
+
40
+ /**
41
+ * @param {string} filePath
42
+ */
43
+ function toBrowserPath(filePath) {
44
+ return filePath.split(path.sep).join('/');
15
45
  }
package/src/ssr.js CHANGED
@@ -1,6 +1,9 @@
1
1
  /** Runs on: imported-js */
2
2
  import { render } from '@lit-labs/ssr';
3
3
  import { collectResult } from '@lit-labs/ssr/lib/render-result.js';
4
+ import { html } from 'lit';
5
+
6
+ export { html, render };
4
7
 
5
8
  /**
6
9
  * @param {string | import('lit').TemplateResult} template
package/src/transform.js CHANGED
@@ -90,10 +90,11 @@ export async function mdToJsSingleDemo(md, demo) {
90
90
 
91
91
  const code = /** @type {{client: string; server: string}} */ (result.data.code);
92
92
 
93
- const litImport = hasTopLevelHtmlBinding(code.server) ? '' : "import { html } from 'lit'";
93
+ const runtimeImport = hasTopLevelHtmlBinding(code.server)
94
+ ? "import { render } from '@rocket/js/ssr.js';"
95
+ : "import { html, render } from '@rocket/js/ssr.js';";
94
96
  const moduleCode = `
95
- import {render} from '@lit-labs/ssr';
96
- ${litImport}
97
+ ${runtimeImport}
97
98
  ${code.server}
98
99
  export function contentFn(data, layout) {
99
100
  data._clientCode = \`${code.client
@@ -117,12 +118,11 @@ export function contentFn(data, layout) {
117
118
  */
118
119
  async function makeJsFile(serverCode, clientCode, markdown, headlines) {
119
120
  const normalizedServerCode = normalizeLayoutExportBindings(serverCode);
120
- const litImport = hasTopLevelHtmlBinding(normalizedServerCode)
121
- ? ''
122
- : "import { html } from 'lit'";
121
+ const runtimeImport = hasTopLevelHtmlBinding(normalizedServerCode)
122
+ ? "import { render } from '@rocket/js/ssr.js';"
123
+ : "import { html, render } from '@rocket/js/ssr.js';";
123
124
  return `
124
- import {render} from '@lit-labs/ssr';
125
- ${litImport}
125
+ ${runtimeImport}
126
126
  ${normalizedServerCode}
127
127
  export function contentFn(data, defaultLayout) {
128
128
  let renderLayout = defaultLayout;
@@ -412,7 +412,13 @@ export function isDemoCodeNode(node) {
412
412
  */
413
413
  function demoExportName(code) {
414
414
  const [, moduleExports] = parseExports(code);
415
- const name = moduleExports[0] && (moduleExports[0].ln || moduleExports[0].n);
415
+ const first = moduleExports[0];
416
+ const name =
417
+ first?.type === 'direct'
418
+ ? first.localName || first.name
419
+ : first?.type === 'reexport'
420
+ ? first.name
421
+ : undefined;
416
422
  if (!name) {
417
423
  throw new Error(
418
424
  'A ```js demo code block must export the demo function, ' +
package/src/wds-plugin.js CHANGED
@@ -129,7 +129,8 @@ export default (include, exclude, resolverPort, options = {}) => {
129
129
  type: rocketIconRuntime.type,
130
130
  };
131
131
  }
132
- const iconAsset = iconAssetStore.get(context.path);
132
+ const iconAsset =
133
+ iconAssetStore.get(context.path) ?? (await iconAssetStore.getWebAwesomeIcon(context.path));
133
134
  if (iconAsset) {
134
135
  return {
135
136
  body: iconAsset.svg,