@terra-symposium/docs 0.1.79 → 0.1.81

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@terra-symposium/docs",
3
- "version": "0.1.79",
3
+ "version": "0.1.81",
4
4
  "description": "Terra Docs workspace and Markdown site generator.",
5
5
  "repository": {
6
6
  "type": "git",
@@ -1,6 +1,6 @@
1
1
  import { terraErrorBoundaryIntegration as __terraErrorBoundaryIntegration } from '@terra-symposium/standards-error-boundaries/astro-integration';
2
2
  import { fileURLToPath } from 'node:url';
3
- import { resolve } from 'node:path';
3
+ import { dirname, resolve } from 'node:path';
4
4
  import { readFileSync } from 'node:fs';
5
5
  import { createRequire } from 'node:module';
6
6
  import { defineConfig } from 'astro/config';
@@ -13,11 +13,9 @@ import { docsContentPlugin } from './docs-content-plugin.mjs';
13
13
 
14
14
  const packageRoot = fileURLToPath(new URL('../', import.meta.url));
15
15
  const requireFromPackage = createRequire(import.meta.url);
16
- const stylingEntry = requireFromPackage.resolve('@terra-symposium/styling');
17
- const requireFromStyling = createRequire(stylingEntry);
18
- const astroManifest = JSON.parse(
19
- readFileSync(requireFromPackage.resolve('astro/package.json'), 'utf8'),
20
- );
16
+ const astroManifestPath = requireFromPackage.resolve('astro/package.json');
17
+ const astroManifest = JSON.parse(readFileSync(astroManifestPath, 'utf8'));
18
+ const astroPackageRoot = dirname(astroManifestPath);
21
19
  const docsManifest = JSON.parse(readFileSync(resolve(packageRoot, 'package.json'), 'utf8'));
22
20
  const projectRoot = process.env.TERRA_DOCS_ROOT ?? process.cwd();
23
21
  const publicDir = resolve(projectRoot, '.terra-docs/public');
@@ -32,7 +30,6 @@ const projectManifest = (() => {
32
30
  }
33
31
  })();
34
32
  const requireFromProject = createRequire(resolve(projectRoot, 'package.json'));
35
-
36
33
  function packageExportAliases() {
37
34
  if (!projectManifest.name || !projectManifest.exports) return [];
38
35
  const escapeRegex = (value) => value.replace(/[|\\{}()[\]^$+?.]/g, '\\$&');
@@ -51,36 +48,53 @@ function packageExportAliases() {
51
48
  }
52
49
 
53
50
  function astroPackageExportAliases() {
54
- return Object.keys(astroManifest.exports)
51
+ return [
52
+ {
53
+ // Astro's MDX renderer imports this internal runtime path, which Astro
54
+ // does not expose through package exports. Resolve it from Docs' Astro.
55
+ find: /^astro\/runtime\/(.+)$/,
56
+ replacement: resolve(astroPackageRoot, 'dist/runtime/$1'),
57
+ },
58
+ ...Object.keys(astroManifest.exports)
55
59
  .filter((key) => key.startsWith('./') && !key.includes('*'))
56
60
  .map((key) => {
57
61
  const specifier = `astro${key.slice(1)}`;
58
- try {
59
- requireFromProject.resolve(specifier);
60
- return null;
61
- } catch {
62
- // Package-owned Astro installation is the fallback for content-only repos.
63
- }
64
62
  return {
65
63
  find: new RegExp(`^${specifier.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')}$`),
66
64
  replacement: requireFromPackage.resolve(specifier),
67
65
  };
68
- })
69
- .filter(Boolean);
66
+ }),
67
+ ];
68
+ }
69
+
70
+ function dependencyManifest(name, resolver = requireFromPackage) {
71
+ let entry;
72
+ try {
73
+ entry = resolver.resolve(name);
74
+ } catch {
75
+ return null;
76
+ }
77
+ let directory = resolve(entry, '..');
78
+ while (directory !== resolve(directory, '..')) {
79
+ const manifestPath = resolve(directory, 'package.json');
80
+ try {
81
+ const manifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
82
+ if (manifest.name === name) return manifest;
83
+ } catch {
84
+ // Keep walking through package subdirectories until the owning manifest is found.
85
+ }
86
+ directory = resolve(directory, '..');
87
+ }
88
+ return null;
70
89
  }
71
90
 
72
91
  function packageDependencyAliases() {
73
92
  return Object.keys(docsManifest.dependencies ?? {})
74
93
  .filter((name) => name !== 'astro')
75
94
  .flatMap((name) => {
76
- const manifestPath = resolve(packageRoot, 'node_modules', name, 'package.json');
77
- let dependencyManifest;
78
- try {
79
- dependencyManifest = JSON.parse(readFileSync(manifestPath, 'utf8'));
80
- } catch {
81
- return [];
82
- }
83
- const subpaths = Object.keys(dependencyManifest.exports ?? {}).filter(
95
+ const manifest = dependencyManifest(name);
96
+ if (!manifest) return [];
97
+ const subpaths = Object.keys(manifest.exports ?? {}).filter(
84
98
  (key) => key.startsWith('./') && !key.includes('*'),
85
99
  );
86
100
  return [name, ...subpaths.map((key) => `${name}${key.slice(1)}`)].flatMap((specifier) => {
@@ -98,6 +112,35 @@ function packageDependencyAliases() {
98
112
  });
99
113
  }
100
114
 
115
+ function stylingDependencyAliases() {
116
+ const stylingManifest =
117
+ projectManifest.name === '@terra-symposium/styling'
118
+ ? projectManifest
119
+ : dependencyManifest('@terra-symposium/styling', requireFromProject);
120
+ if (!stylingManifest) return [];
121
+ return Object.keys(stylingManifest.dependencies ?? {})
122
+ .filter((name) => name !== 'react' && name !== 'react-dom' && (name.includes('react') || ['cmdk', 'vaul'].includes(name)))
123
+ .flatMap((name) => {
124
+ const manifest = dependencyManifest(name, requireFromProject);
125
+ if (!manifest) return [];
126
+ const subpaths = Object.keys(manifest.exports ?? {}).filter(
127
+ (key) => key.startsWith('./') && !key.includes('*'),
128
+ );
129
+ return [name, ...subpaths.map((key) => `${name}${key.slice(1)}`)].flatMap((specifier) => {
130
+ try {
131
+ return [
132
+ {
133
+ find: new RegExp(`^${specifier.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')}$`),
134
+ replacement: requireFromProject.resolve(specifier),
135
+ },
136
+ ];
137
+ } catch {
138
+ return [];
139
+ }
140
+ });
141
+ });
142
+ }
143
+
101
144
  function sharedReactAliases() {
102
145
  const specifiers = [
103
146
  'react',
@@ -106,14 +149,32 @@ function sharedReactAliases() {
106
149
  'react/jsx-runtime',
107
150
  'react-dom',
108
151
  'react-dom/client',
109
- 'react-dom/server',
110
152
  ];
111
- return specifiers.map((specifier) => {
153
+ return [
154
+ ...specifiers.map((specifier) => {
155
+ let replacement;
156
+ try {
157
+ // Externalized component dependencies resolve React from the consumer's
158
+ // installation during SSR. Prefer that same copy for bundled imports.
159
+ replacement = requireFromProject.resolve(specifier);
160
+ } catch {
161
+ // Content-only repositories can rely on the React runtime shipped by Docs.
162
+ replacement = requireFromPackage.resolve(specifier);
163
+ }
112
164
  return {
113
165
  find: new RegExp(`^${specifier.replace(/[|\\{}()[\]^$+?.]/g, '\\$&')}$`),
114
- replacement: requireFromPackage.resolve(specifier),
166
+ replacement,
115
167
  };
116
- });
168
+ }),
169
+ {
170
+ // The Docs editor imports `react-dom/server` for Markdown previews. Its
171
+ // Node entrypoint pulls `util.TextEncoder` into browser chunks, where
172
+ // `util` is externalized and the Docs workspace fails to hydrate. The
173
+ // browser server renderer also works during Node prerendering.
174
+ find: /^react-dom\/server$/,
175
+ replacement: requireFromProject.resolve('react-dom/server.browser'),
176
+ },
177
+ ];
117
178
  }
118
179
 
119
180
  export default defineConfig({
@@ -139,12 +200,12 @@ export default defineConfig({
139
200
  ...sharedReactAliases(),
140
201
  ...astroPackageExportAliases(),
141
202
  ...packageDependencyAliases(),
203
+ ...stylingDependencyAliases(),
142
204
  ],
143
205
  dedupe: ['react', 'react-dom'],
144
206
  },
145
207
  ssr: {
146
- // Bundle React peer consumers with the package-owned React runtime so
147
- // static rendering does not load two copies across pnpm virtual stores.
208
+ // Bundle the shared UI packages while retaining their peer runtime.
148
209
  noExternal: ['@terra-symposium/styling', '@terra-symposium/standards-error-boundaries'],
149
210
  },
150
211
  server: { fs: { allow: [projectRoot, packageRoot] } },
@@ -9,9 +9,6 @@ IMPORTS
9
9
  // API
10
10
  import { useMemo, useState } from 'react';
11
11
 
12
- // COMPONENTS
13
- import { Button, Input, Label } from '@terra-symposium/styling/ui';
14
-
15
12
  /*//////////////////////////////////////////////////
16
13
  CONFIG
17
14
  //////////////////////////////////////////////////*/
@@ -133,8 +130,11 @@ export function SkillsExplorer({ skills }: { skills: SkillRecord[] }) {
133
130
  className="max-h-[78vh] min-w-0 overflow-auto rounded-xl border border-gray-6 bg-gray-1 p-3"
134
131
  >
135
132
  <div>
136
- <Label htmlFor="skills-explorer-filter">Filter skills</Label>
137
- <Input
133
+ <label className="text-sm font-medium text-gray-11" htmlFor="skills-explorer-filter">
134
+ Filter skills
135
+ </label>
136
+ <input
137
+ className="mt-1 w-full rounded-md border border-gray-6 bg-gray-1 px-3 py-2 text-sm text-gray-12 shadow-sm outline-none placeholder:text-gray-9 focus-visible:ring-2 focus-visible:ring-primary-8"
138
138
  id="skills-explorer-filter"
139
139
  onChange={(event) => setQuery(event.currentTarget.value)}
140
140
  placeholder="Name, topic, or text"
@@ -154,17 +154,14 @@ export function SkillsExplorer({ skills }: { skills: SkillRecord[] }) {
154
154
  <ul className="m-0 grid list-none gap-1 p-0">
155
155
  {entries.map((skill) => (
156
156
  <li key={skill.sourcePath}>
157
- <Button
158
- aria-current={
159
- selectedSkill?.sourcePath === skill.sourcePath ? 'true' : undefined
160
- }
161
- className="h-auto w-full justify-start px-3 py-2 text-left"
162
- onClick={() => setActivePath(skill.sourcePath)}
163
- variant={
157
+ <button
158
+ aria-pressed={selectedSkill?.sourcePath === skill.sourcePath}
159
+ className={`grid h-auto w-full min-w-0 justify-start rounded-md border px-3 py-2 text-left transition-colors focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-primary-8 ${
164
160
  selectedSkill?.sourcePath === skill.sourcePath
165
- ? 'primary-tonal'
166
- : 'gray-ghost'
167
- }
161
+ ? 'border-primary-7 bg-primary-3 text-primary-12'
162
+ : 'border-transparent bg-transparent text-gray-11 hover:border-gray-6 hover:bg-gray-2 hover:text-gray-12'
163
+ }`}
164
+ onClick={() => setActivePath(skill.sourcePath)}
168
165
  type="button"
169
166
  >
170
167
  <span className="grid min-w-0 gap-1">
@@ -176,7 +173,7 @@ export function SkillsExplorer({ skills }: { skills: SkillRecord[] }) {
176
173
  {skill.directory}
177
174
  </span>
178
175
  </span>
179
- </Button>
176
+ </button>
180
177
  </li>
181
178
  ))}
182
179
  </ul>