@orbytes/astrolab 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 (94) hide show
  1. package/LICENSE +37 -0
  2. package/README.md +410 -0
  3. package/bin/lab-cull.mjs +401 -0
  4. package/bin/pin-gallery.mjs +121 -0
  5. package/defaults.mjs +120 -0
  6. package/dist/core/astro-integration.js +130 -0
  7. package/dist/core/index.js +6 -0
  8. package/dist/core/lib-paths.js +29 -0
  9. package/dist/core/options.js +173 -0
  10. package/dist/core/utils/get-exports.js +52 -0
  11. package/dist/core/utils/invariant.js +12 -0
  12. package/dist/core/utils/kebab-case.js +15 -0
  13. package/dist/core/utils/path-builder.js +22 -0
  14. package/dist/core/utils/path.js +53 -0
  15. package/dist/core/virtual-module/get-story-modules.js +60 -0
  16. package/dist/core/virtual-module/story-modules.js +8 -0
  17. package/dist/core/virtual-module/virtual-module-ids.js +22 -0
  18. package/dist/core/virtual-module/virtual-routes.js +83 -0
  19. package/dist/core/virtual-module/vite-plugin.js +98 -0
  20. package/docs/PIN-CONTRACT.md +263 -0
  21. package/docs/PIN.md +429 -0
  22. package/index.d.ts +279 -0
  23. package/index.mjs +347 -0
  24. package/package.json +93 -0
  25. package/src/Empty.astro +4 -0
  26. package/src/Home.astro +298 -0
  27. package/src/LabHead.astro +1102 -0
  28. package/src/core/LICENSE-astrobook +166 -0
  29. package/src/core/astro-integration.ts +166 -0
  30. package/src/core/client.ts +89 -0
  31. package/src/core/index.ts +7 -0
  32. package/src/core/lib/components/empty.astro +1 -0
  33. package/src/core/lib/components/head.astro +1 -0
  34. package/src/core/lib/components/home.astro +8 -0
  35. package/src/core/lib/components/with-decorators.astro +22 -0
  36. package/src/core/lib/pages/app.astro +19 -0
  37. package/src/core/lib/pages/preview.astro +17 -0
  38. package/src/core/lib/pages/story.astro +16 -0
  39. package/src/core/lib-paths.ts +72 -0
  40. package/src/core/options.ts +262 -0
  41. package/src/core/utils/get-exports.ts +59 -0
  42. package/src/core/utils/invariant.ts +13 -0
  43. package/src/core/utils/kebab-case.ts +30 -0
  44. package/src/core/utils/path-builder.ts +45 -0
  45. package/src/core/utils/path.ts +80 -0
  46. package/src/core/virtual-module/get-story-modules.ts +110 -0
  47. package/src/core/virtual-module/story-modules.ts +9 -0
  48. package/src/core/virtual-module/virtual-module-ids.ts +17 -0
  49. package/src/core/virtual-module/virtual-routes.ts +130 -0
  50. package/src/core/virtual-module/vite-plugin.ts +125 -0
  51. package/src/pin/board.mjs +1521 -0
  52. package/src/pin/index.mjs +666 -0
  53. package/src/pin/shot.mjs +427 -0
  54. package/src/pin/source-stamp.mjs +159 -0
  55. package/src/pin/tickets.mjs +697 -0
  56. package/src/pin/toolbar.js +3181 -0
  57. package/src/shell/Browse.astro +371 -0
  58. package/src/shell/CardGrid.astro +297 -0
  59. package/src/shell/Viewport.astro +1330 -0
  60. package/src/shell/index.json.ts +12 -0
  61. package/src/shell/lab-index.ts +344 -0
  62. package/src/shell/lab-params.ts +245 -0
  63. package/src/shell/live-files.mjs +164 -0
  64. package/src/shell/marks.mjs +136 -0
  65. package/src/types/index.ts +6 -0
  66. package/src/types/types.ts +239 -0
  67. package/src/types/virtual.d.ts +29 -0
  68. package/src/ui/components/app.astro +13 -0
  69. package/src/ui/components/build-path.ts +13 -0
  70. package/src/ui/components/build-tree.ts +108 -0
  71. package/src/ui/components/collapse-duration.ts +28 -0
  72. package/src/ui/components/compress-terms.ts +10 -0
  73. package/src/ui/components/dashboard-layout.astro +39 -0
  74. package/src/ui/components/home.astro +65 -0
  75. package/src/ui/components/layout.astro +110 -0
  76. package/src/ui/components/preview-layout.astro +109 -0
  77. package/src/ui/components/sidebar-button-fullscreen.astro +38 -0
  78. package/src/ui/components/sidebar-button-search.astro +23 -0
  79. package/src/ui/components/sidebar-button-theme.astro +9 -0
  80. package/src/ui/components/sidebar-button.astro +24 -0
  81. package/src/ui/components/sidebar-resize-handle.astro +74 -0
  82. package/src/ui/components/sidebar-search-panel.astro +41 -0
  83. package/src/ui/components/sidebar-search-script.ts +103 -0
  84. package/src/ui/components/sidebar-title.astro +17 -0
  85. package/src/ui/components/sidebar-tree-node.astro +143 -0
  86. package/src/ui/components/sidebar-tree.astro +84 -0
  87. package/src/ui/components/sidebar.astro +29 -0
  88. package/src/ui/components/theme-message.ts +26 -0
  89. package/src/ui/components/theme-script.astro +71 -0
  90. package/src/ui/components/theme-toggle.astro +63 -0
  91. package/src/ui/components/theme.ts +32 -0
  92. package/src/ui/index.ts +4 -0
  93. package/src/ui/lab.css +549 -0
  94. package/virtual.d.ts +42 -0
@@ -0,0 +1,130 @@
1
+ // Vendored from astrobook 0.13.3 (virtual-module/virtual-routes.ts) — see ../LICENSE-astrobook.
2
+ // Divergence: relative imports carry the .ts extension so node can load this graph directly
3
+ // (upstream is bundled before it ships; this package is not).
4
+ // Divergence: the specifiers this file emits into every generated route are absolute paths into ./lib-paths.ts rather
5
+ // than bare `astrobook/...` ids — see that file for why.
6
+ import path from 'node:path'
7
+
8
+ import type { AstroIntegrationLogger } from 'astro'
9
+ import slash from 'slash'
10
+
11
+ import {
12
+ CLIENT_MODULE,
13
+ PREVIEW_PAGE,
14
+ STORY_PAGE,
15
+ WITH_DECORATORS_COMPONENT,
16
+ } from '../lib-paths.ts'
17
+ import type { Story, StoryModule } from '../../types/types.ts'
18
+ import { invariant } from '../utils/invariant.ts'
19
+ import type { PathBuilder } from '../utils/path-builder.ts'
20
+
21
+ import { getStoryModules } from './get-story-modules.ts'
22
+
23
+ export interface VirtualRoute {
24
+ pattern: string
25
+ /**
26
+ * The absolute path to the virtual entrypoint file. Posix slash format.
27
+ *
28
+ * It's important to use an absolute path here because we must ensure that we
29
+ * only have one `id`, so that Astro's CSS plugin can find the correct CSS
30
+ * content in the following line.
31
+ * https://github.com/withastro/astro/blob/bc2796436dc3810e988c27b71b7a66fcb1ae8bda/packages/astro/src/core/build/plugins/plugin-css.ts#L144
32
+ */
33
+ entrypoint: string
34
+ storyModule: StoryModule
35
+ story: Story
36
+ props: {
37
+ hasSidebar: boolean
38
+ story: string
39
+ }
40
+ }
41
+
42
+ export async function getVirtualRoutes(
43
+ rootDir: string,
44
+ codegenDir: string,
45
+ logger: AstroIntegrationLogger,
46
+ dashboardSubpath: string,
47
+ previewSubpath: string,
48
+ buildPath: PathBuilder,
49
+ ): Promise<Map<string, VirtualRoute>> {
50
+ const routes: VirtualRoute[] = []
51
+ const storyModules = await getStoryModules(rootDir)
52
+
53
+ for (const storyModule of storyModules) {
54
+ logger.debug(
55
+ `Found ${storyModule.stories.length} stories in ${storyModule.importPath}`,
56
+ )
57
+ for (const story of storyModule.stories) {
58
+ invariant(
59
+ !story.id.startsWith('..'),
60
+ `Story ID cannot start with '..', but got '${story.id}'`,
61
+ )
62
+ invariant(
63
+ !story.id.startsWith('/'),
64
+ `Story ID cannot start with '/', but got '${story.id}'`,
65
+ )
66
+ routes.push(
67
+ {
68
+ pattern: buildPath(dashboardSubpath, story.id),
69
+ entrypoint: slash(
70
+ path.resolve(codegenDir, 'dashboard', story.id + '.astro'),
71
+ ),
72
+ storyModule,
73
+ story,
74
+ props: { hasSidebar: true, story: story.id },
75
+ },
76
+ {
77
+ pattern: buildPath(previewSubpath, story.id),
78
+ entrypoint: slash(
79
+ path.resolve(codegenDir, 'stories', story.id + '.astro'),
80
+ ),
81
+ storyModule,
82
+ story,
83
+ props: { hasSidebar: false, story: story.id },
84
+ },
85
+ )
86
+ }
87
+ }
88
+
89
+ logger.info(`Found ${routes.length} stories in ${storyModules.length} files`)
90
+
91
+ return new Map(routes.map((route) => [route.pattern, route]))
92
+ }
93
+
94
+ export function createVirtualRouteComponent(route: VirtualRoute): string {
95
+ // A module name that's useful for debugging and error messages
96
+ const modName = `astrobook_module_${route.storyModule.id}`
97
+ .replaceAll(/\W+/g, '_')
98
+ .replaceAll(/_+/g, '_')
99
+ .replace(/_+$/, '')
100
+
101
+ // Divergence: upstream emits STORY_PAGE for both routes. The dashboard route (hasSidebar) is
102
+ // chrome plus preview and keeps it; the bare story route gets PREVIEW_PAGE, which carries the
103
+ // consumer's CSS and none of the lab's — decided 2026-09-22, see ../../ui/lab.css.
104
+ const page = route.props.hasSidebar ? STORY_PAGE : PREVIEW_PAGE
105
+
106
+ return `
107
+ ---
108
+ // Automatically generated by Astrobook
109
+
110
+ import StoryPage from '${page}';
111
+ import WithDecorators from '${WITH_DECORATORS_COMPONENT}';
112
+ import { parseStoryDefaultExport, parseStoryNamedExport } from '${CLIENT_MODULE}';
113
+
114
+ import * as ${modName} from '${route.storyModule.importPath}';
115
+
116
+ const { isAstroComponent } = parseStoryDefaultExport(${modName}, '${route.storyModule.importPath}');
117
+ const { decorators, args } = parseStoryNamedExport(${modName}, '${route.storyModule.importPath}', '${route.story.name}');
118
+ ---
119
+
120
+ <StoryPage story={'${route.props.story}'} hasSidebar={${route.props.hasSidebar}}>
121
+ <WithDecorators decorators={decorators}>
122
+ {isAstroComponent ? (
123
+ <${modName}.default.component {...args} />
124
+ ) : (
125
+ <${modName}.default.component {...args} client:load />
126
+ )}
127
+ </WithDecorators>
128
+ </StoryPage>
129
+ `.trim()
130
+ }
@@ -0,0 +1,125 @@
1
+ // Vendored from astrobook 0.13.3 (virtual-module/vite-plugin.ts) — see ../LICENSE-astrobook.
2
+ // Divergence: relative imports carry the .ts extension so node can load this graph directly
3
+ // (upstream is bundled before it ships; this package is not).
4
+ import { resolve } from 'node:path'
5
+ import { fileURLToPath } from 'node:url'
6
+
7
+ import type { GlobalConfig } from '../../types/types.ts'
8
+ import type { AstroConfig, AstroIntegrationLogger } from 'astro'
9
+ import type { Plugin, ViteDevServer } from 'vite'
10
+
11
+ import { loadStoryModules } from './story-modules.ts'
12
+ import {
13
+ COMPONENT_HEAD_ID,
14
+ COMPONENT_HEAD_RESOLVED_ID,
15
+ COMPONENT_HOME_ID,
16
+ COMPONENT_HOME_RESOLVED_ID,
17
+ STORY_MODULES_ID,
18
+ STORY_MODULES_RESOLVED_ID,
19
+ GLOBAL_CONFIG_ID,
20
+ GLOBAL_CONFIG_RESOLVED_ID,
21
+ USER_CSS_RESOLVED_ID,
22
+ USER_CSS_ID,
23
+ } from './virtual-module-ids.ts'
24
+
25
+ export function createVirtualFilesPlugin(
26
+ rootDir: string,
27
+ config: GlobalConfig,
28
+ astroConfig: AstroConfig,
29
+ logger: AstroIntegrationLogger,
30
+ ): Plugin {
31
+ const root = astroConfig.root
32
+
33
+ /**
34
+ * Resolves module IDs to a usable format:
35
+ * - Relative paths (e.g. `'./module.js'`) are resolved against `base` and formatted as an absolute path.
36
+ * - Package identifiers (e.g. `'module'`) are returned unchanged.
37
+ *
38
+ * By default, `base` is the project root directory.
39
+ */
40
+ function resolveId(id: string, base = root) {
41
+ return JSON.stringify(
42
+ id.startsWith('.') ? resolve(fileURLToPath(base), id) : id,
43
+ )
44
+ }
45
+
46
+ let storyModules: string | undefined
47
+ let needToReload = false
48
+ let watcherInitialized = false
49
+
50
+ async function getStoryModules(): Promise<string> {
51
+ const newStoryModules = await loadStoryModules(rootDir)
52
+ if (storyModules && newStoryModules !== storyModules) {
53
+ needToReload = true
54
+ }
55
+ storyModules = newStoryModules
56
+ return storyModules
57
+ }
58
+
59
+ async function restartServerIfNeeded(server: ViteDevServer) {
60
+ await getStoryModules()
61
+ if (needToReload) {
62
+ needToReload = false
63
+ logger.info(`Restarting`)
64
+ await server.restart()
65
+ }
66
+ }
67
+
68
+ function initializeNewFileWatcher(server: ViteDevServer) {
69
+ if (watcherInitialized || !server.watcher) return
70
+ watcherInitialized = true
71
+
72
+ server.watcher.on('add', async (filePath: string) => {
73
+ if (filePath.includes('.stories.')) {
74
+ logger.debug(`Potential story file added: ${filePath}`)
75
+ await restartServerIfNeeded(server)
76
+ }
77
+ })
78
+
79
+ server.watcher.on('unlink', async (filePath: string) => {
80
+ if (filePath.includes('.stories.')) {
81
+ logger.debug(`Potential story file deleted: ${filePath}`)
82
+ await restartServerIfNeeded(server)
83
+ }
84
+ })
85
+
86
+ logger.debug(`Initialized file watcher for story files`)
87
+ }
88
+
89
+ return {
90
+ name: 'astrobook/virtual-files',
91
+ resolveId(id) {
92
+ switch (id) {
93
+ case STORY_MODULES_ID:
94
+ return STORY_MODULES_RESOLVED_ID
95
+ case GLOBAL_CONFIG_ID:
96
+ return GLOBAL_CONFIG_RESOLVED_ID
97
+ case COMPONENT_HEAD_ID:
98
+ return COMPONENT_HEAD_RESOLVED_ID
99
+ case COMPONENT_HOME_ID:
100
+ return COMPONENT_HOME_RESOLVED_ID
101
+ case USER_CSS_ID:
102
+ return USER_CSS_RESOLVED_ID
103
+ }
104
+ },
105
+ load(id) {
106
+ switch (id) {
107
+ case STORY_MODULES_RESOLVED_ID:
108
+ return getStoryModules()
109
+ case GLOBAL_CONFIG_RESOLVED_ID:
110
+ return `const config = ${JSON.stringify(config)}; export default config;`
111
+ case COMPONENT_HEAD_RESOLVED_ID:
112
+ return `export { default } from ${resolveId(config.head)};`
113
+ case COMPONENT_HOME_RESOLVED_ID:
114
+ return `export { default } from ${resolveId(config.home)};`
115
+ case USER_CSS_RESOLVED_ID:
116
+ return config.css.map((id) => `import ${resolveId(id)};`).join('')
117
+ }
118
+ },
119
+ async handleHotUpdate(ctx) {
120
+ const { server } = ctx
121
+ initializeNewFileWatcher(server)
122
+ await restartServerIfNeeded(server)
123
+ },
124
+ }
125
+ }