@1agh/maude 0.15.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/LICENSE +21 -0
- package/README.md +166 -0
- package/cli/bin/maude.exe +15 -0
- package/cli/bin/maude.mjs +45 -0
- package/cli/bin/mdcc.exe +10 -0
- package/cli/bin/mdcc.mjs +7 -0
- package/cli/cli-wrapper.cjs +67 -0
- package/cli/commands/config.mjs +94 -0
- package/cli/commands/design.mjs +386 -0
- package/cli/commands/help.mjs +57 -0
- package/cli/commands/init.mjs +178 -0
- package/cli/commands/version.mjs +7 -0
- package/cli/install.cjs +113 -0
- package/cli/lib/argv.mjs +37 -0
- package/cli/lib/argv.test.mjs +46 -0
- package/cli/lib/copy-tree.mjs +78 -0
- package/package.json +94 -0
- package/plugins/design/dev-server/annotations-context-toolbar.tsx +397 -0
- package/plugins/design/dev-server/annotations-layer.tsx +1717 -0
- package/plugins/design/dev-server/api.ts +674 -0
- package/plugins/design/dev-server/bin/_screenshot-playwright.mjs +50 -0
- package/plugins/design/dev-server/bin/bootstrap-check.sh +83 -0
- package/plugins/design/dev-server/bin/canvas-edit.sh +48 -0
- package/plugins/design/dev-server/bin/handoff.sh +27 -0
- package/plugins/design/dev-server/bin/screenshot.sh +232 -0
- package/plugins/design/dev-server/bin/server-up.sh +135 -0
- package/plugins/design/dev-server/bin/slug.sh +22 -0
- package/plugins/design/dev-server/bin/smoke.sh +272 -0
- package/plugins/design/dev-server/build.ts +267 -0
- package/plugins/design/dev-server/canvas-build.ts +219 -0
- package/plugins/design/dev-server/canvas-edit.ts +388 -0
- package/plugins/design/dev-server/canvas-header.ts +165 -0
- package/plugins/design/dev-server/canvas-icons.tsx +131 -0
- package/plugins/design/dev-server/canvas-lib-inline.ts +260 -0
- package/plugins/design/dev-server/canvas-lib-resolver.ts +85 -0
- package/plugins/design/dev-server/canvas-lib.tsx +1995 -0
- package/plugins/design/dev-server/canvas-meta.schema.json +181 -0
- package/plugins/design/dev-server/canvas-pipeline.ts +270 -0
- package/plugins/design/dev-server/canvas-shell.tsx +813 -0
- package/plugins/design/dev-server/client/app.jsx +2027 -0
- package/plugins/design/dev-server/client/hmr.mjs +85 -0
- package/plugins/design/dev-server/client/iframe-lazy.mjs +121 -0
- package/plugins/design/dev-server/client/index.html +15 -0
- package/plugins/design/dev-server/client/styles/0-reset.css +18 -0
- package/plugins/design/dev-server/client/styles/1-tokens.css +297 -0
- package/plugins/design/dev-server/client/styles/2-layout.css +35 -0
- package/plugins/design/dev-server/client/styles/3-shell.css +906 -0
- package/plugins/design/dev-server/client/styles/4-components.css +1268 -0
- package/plugins/design/dev-server/client/styles/5-utilities.css +4 -0
- package/plugins/design/dev-server/client/styles/_index.css +24 -0
- package/plugins/design/dev-server/client/styles.css +1419 -0
- package/plugins/design/dev-server/config.schema.json +147 -0
- package/plugins/design/dev-server/context-menu.tsx +343 -0
- package/plugins/design/dev-server/context.ts +173 -0
- package/plugins/design/dev-server/dist/client.bundle.js +20323 -0
- package/plugins/design/dev-server/dist/styles.css +2875 -0
- package/plugins/design/dev-server/examples/README.md +9 -0
- package/plugins/design/dev-server/examples/perf-100-artboards.tsx +113 -0
- package/plugins/design/dev-server/fs-watch.ts +63 -0
- package/plugins/design/dev-server/handoff.ts +721 -0
- package/plugins/design/dev-server/history.ts +125 -0
- package/plugins/design/dev-server/hmr-broadcast.ts +114 -0
- package/plugins/design/dev-server/http.ts +413 -0
- package/plugins/design/dev-server/input-router.tsx +485 -0
- package/plugins/design/dev-server/inspect.ts +365 -0
- package/plugins/design/dev-server/locator.ts +159 -0
- package/plugins/design/dev-server/mem.ts +97 -0
- package/plugins/design/dev-server/runtime-bundle.ts +235 -0
- package/plugins/design/dev-server/server.mjs +1246 -0
- package/plugins/design/dev-server/server.ts +131 -0
- package/plugins/design/dev-server/test/_helpers.ts +81 -0
- package/plugins/design/dev-server/test/active-state.test.ts +145 -0
- package/plugins/design/dev-server/test/annotations-api.test.ts +146 -0
- package/plugins/design/dev-server/test/annotations-layer.test.ts +419 -0
- package/plugins/design/dev-server/test/binary-smoke.test.ts +47 -0
- package/plugins/design/dev-server/test/bundle-smoke.test.ts +29 -0
- package/plugins/design/dev-server/test/canvas-build.test.ts +78 -0
- package/plugins/design/dev-server/test/canvas-edit.test.ts +139 -0
- package/plugins/design/dev-server/test/canvas-header.test.ts +127 -0
- package/plugins/design/dev-server/test/canvas-lib-inline.test.ts +146 -0
- package/plugins/design/dev-server/test/canvas-lib-resolver.test.ts +112 -0
- package/plugins/design/dev-server/test/canvas-meta-api.test.ts +236 -0
- package/plugins/design/dev-server/test/canvas-pipeline.test.ts +180 -0
- package/plugins/design/dev-server/test/canvas-route.test.ts +176 -0
- package/plugins/design/dev-server/test/fs-watch.test.ts +41 -0
- package/plugins/design/dev-server/test/handoff-static-frames.test.ts +215 -0
- package/plugins/design/dev-server/test/handoff.test.ts +281 -0
- package/plugins/design/dev-server/test/history-rollback.test.ts +62 -0
- package/plugins/design/dev-server/test/hmr-broadcast.test.ts +108 -0
- package/plugins/design/dev-server/test/input-router.test.ts +316 -0
- package/plugins/design/dev-server/test/locator.test.ts +214 -0
- package/plugins/design/dev-server/test/perf-harness.ts +193 -0
- package/plugins/design/dev-server/test/phase-3.6-smoke.test.ts +77 -0
- package/plugins/design/dev-server/test/runtime-bundle.test.ts +69 -0
- package/plugins/design/dev-server/test/server-lifecycle.test.ts +28 -0
- package/plugins/design/dev-server/test/tool-palette.test.tsx +55 -0
- package/plugins/design/dev-server/test/use-annotation-selection.test.tsx +77 -0
- package/plugins/design/dev-server/test/use-artboard-drag.test.ts +325 -0
- package/plugins/design/dev-server/test/use-selection-set.test.tsx +166 -0
- package/plugins/design/dev-server/test/use-snap-guides.test.ts +190 -0
- package/plugins/design/dev-server/test/use-tool-mode.test.tsx +93 -0
- package/plugins/design/dev-server/test/ws-handshake.test.ts +33 -0
- package/plugins/design/dev-server/tool-palette.tsx +278 -0
- package/plugins/design/dev-server/tsconfig.json +26 -0
- package/plugins/design/dev-server/use-annotation-selection.tsx +92 -0
- package/plugins/design/dev-server/use-annotations-visibility.tsx +43 -0
- package/plugins/design/dev-server/use-artboard-drag.tsx +445 -0
- package/plugins/design/dev-server/use-selection-set.tsx +224 -0
- package/plugins/design/dev-server/use-snap-guides.tsx +215 -0
- package/plugins/design/dev-server/use-tool-mode.tsx +114 -0
- package/plugins/design/dev-server/ws.ts +90 -0
- package/plugins/design/templates/_shell.html +177 -0
- package/plugins/design/templates/canvas.tsx.template +54 -0
- package/plugins/design/templates/design-system-inspiration/_MAPPING.md +277 -0
- package/plugins/design/templates/design-system-inspiration/_README.md +71 -0
- package/plugins/design/templates/design-system-inspiration/audience-consumer/components-banner.html +68 -0
- package/plugins/design/templates/design-system-inspiration/audience-consumer/components-empty-state-generous.html +39 -0
- package/plugins/design/templates/design-system-inspiration/audience-consumer/components-feature-grid.html +62 -0
- package/plugins/design/templates/design-system-inspiration/audience-consumer/components-marketing-card.html +63 -0
- package/plugins/design/templates/design-system-inspiration/audience-consumer/components-testimonial.html +80 -0
- package/plugins/design/templates/design-system-inspiration/audience-developer/components-code-block.html +71 -0
- package/plugins/design/templates/design-system-inspiration/audience-developer/components-diff-view.html +65 -0
- package/plugins/design/templates/design-system-inspiration/audience-developer/components-log-stream.html +62 -0
- package/plugins/design/templates/design-system-inspiration/audience-developer/components-monospace-table.html +57 -0
- package/plugins/design/templates/design-system-inspiration/audience-developer/components-terminal-pane.html +67 -0
- package/plugins/design/templates/design-system-inspiration/audience-developer/type-mono.html +57 -0
- package/plugins/design/templates/design-system-inspiration/audience-pro/colors-presence.html +74 -0
- package/plugins/design/templates/design-system-inspiration/audience-pro/components-command-palette.html +90 -0
- package/plugins/design/templates/design-system-inspiration/audience-pro/components-keyboard.html +51 -0
- package/plugins/design/templates/design-system-inspiration/audience-pro/components-list.html +89 -0
- package/plugins/design/templates/design-system-inspiration/audience-pro/components-shortcuts-overlay.html +85 -0
- package/plugins/design/templates/design-system-inspiration/audience-pro/components-toast-menu.html +80 -0
- package/plugins/design/templates/design-system-inspiration/core/INDEX.md.tpl +25 -0
- package/plugins/design/templates/design-system-inspiration/core/README.orchestration.md.tpl +71 -0
- package/plugins/design/templates/design-system-inspiration/core/README.philosophy.md.tpl +77 -0
- package/plugins/design/templates/design-system-inspiration/core/SKILL.md.tpl +50 -0
- package/plugins/design/templates/design-system-inspiration/core/colors_and_type.css.tpl +111 -0
- package/plugins/design/templates/design-system-inspiration/core/config.json.tpl +28 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/_layout.css +113 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/colors-accent.html +48 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/colors-surfaces.html +49 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/colors-text.html +52 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/components-buttons.html +83 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/components-cards.html +79 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/components-inputs.html +82 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/motion.html +66 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/spacing-scale.html +53 -0
- package/plugins/design/templates/design-system-inspiration/core/preview/type-scale.html +62 -0
- package/plugins/design/templates/design-system-inspiration/foundations/borders.html +39 -0
- package/plugins/design/templates/design-system-inspiration/foundations/elevation.html +46 -0
- package/plugins/design/templates/design-system-inspiration/foundations/focus.html +48 -0
- package/plugins/design/templates/design-system-inspiration/foundations/grid.html +51 -0
- package/plugins/design/templates/design-system-inspiration/foundations/iconography.html +73 -0
- package/plugins/design/templates/design-system-inspiration/foundations/opacity.html +45 -0
- package/plugins/design/templates/design-system-inspiration/foundations/radii.html +40 -0
- package/plugins/design/templates/design-system-inspiration/foundations/selection.html +45 -0
- package/plugins/design/templates/design-system-inspiration/meta/accessibility.html +62 -0
- package/plugins/design/templates/design-system-inspiration/meta/i18n.html +73 -0
- package/plugins/design/templates/design-system-inspiration/meta/presence-multiplayer.html +71 -0
- package/plugins/design/templates/design-system-inspiration/meta/tokens-index.html +80 -0
- package/plugins/design/templates/design-system-inspiration/patterns/patterns-auth.html +78 -0
- package/plugins/design/templates/design-system-inspiration/patterns/patterns-data-density.html +61 -0
- package/plugins/design/templates/design-system-inspiration/patterns/patterns-error-pages.html +70 -0
- package/plugins/design/templates/design-system-inspiration/patterns/patterns-form-layouts.html +70 -0
- package/plugins/design/templates/design-system-inspiration/patterns/patterns-onboarding.html +71 -0
- package/plugins/design/templates/design-system-inspiration/patterns/patterns-pricing.html +83 -0
- package/plugins/design/templates/design-system-inspiration/platform-desktop/components-resize-panels.html +63 -0
- package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-index.html +55 -0
- package/plugins/design/templates/design-system-inspiration/platform-desktop/ui_kits-desktop-showcase.html +302 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/components-bottom-sheet.html +63 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/components-pull-to-refresh.html +74 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/components-segmented-control.html +51 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/components-tab-bar.html +57 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-index.html +58 -0
- package/plugins/design/templates/design-system-inspiration/platform-mobile/ui_kits-mobile-showcase.html +237 -0
- package/plugins/design/templates/design-system-inspiration/status/colors-status.html +49 -0
- package/plugins/design/templates/design-system-inspiration/status/components-status.html +63 -0
- package/plugins/design/templates/design-system-inspiration/status/skeletons.html +74 -0
- package/plugins/design/templates/design-system-inspiration/theme-both/colors-themes-side-by-side.html +59 -0
- package/plugins/design/templates/design-system-inspiration/universal/components-callout.html +74 -0
- package/plugins/design/templates/design-system-inspiration/universal/components-dialogs.html +81 -0
- package/plugins/design/templates/design-system-inspiration/universal/components-tables.html +101 -0
- package/plugins/design/templates/design-system-inspiration/universal/components-toggles.html +74 -0
- package/plugins/design/templates/design-system-inspiration/universal/components-tooltips.html +74 -0
- package/plugins/design/templates/design-system-inspiration/universal/empty-state.html.tpl +34 -0
- package/plugins/design/templates/design-system-inspiration/universal/logo.html +42 -0
- package/plugins/design/templates/ds-specimen.tsx.template +59 -0
- package/plugins/flow/.claude-plugin/config.schema.json +398 -0
- package/plugins/flow/README.md +45 -0
- package/plugins/flow/templates/ai-skeleton/INDEX.md +50 -0
- package/plugins/flow/templates/ai-skeleton/README.md +46 -0
- package/plugins/flow/templates/ai-skeleton/browser/har/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/browser/snapshots/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/business/README.md +12 -0
- package/plugins/flow/templates/ai-skeleton/context/README.md +12 -0
- package/plugins/flow/templates/ai-skeleton/decisions/README.md +20 -0
- package/plugins/flow/templates/ai-skeleton/design-import/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/dev-logs/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/device/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/docs/README.md +5 -0
- package/plugins/flow/templates/ai-skeleton/logs/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/logs/README.md +13 -0
- package/plugins/flow/templates/ai-skeleton/plans/README.md +16 -0
- package/plugins/flow/templates/ai-skeleton/plans/archive/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/release-guide.md +35 -0
- package/plugins/flow/templates/ai-skeleton/reviews/README.md +15 -0
- package/plugins/flow/templates/ai-skeleton/scenarios/README.md +26 -0
- package/plugins/flow/templates/ai-skeleton/scenarios/_lib/.gitkeep +0 -0
- package/plugins/flow/templates/ai-skeleton/state/STATE.md +25 -0
- package/plugins/flow/templates/ai-skeleton/templates/HANDOFF.md +32 -0
- package/plugins/flow/templates/ai-skeleton/workflows.config.json +74 -0
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
// canvas-edit.ts — Phase 3.6 Task 5. AST-aware single-attribute edits on TSX
|
|
2
|
+
// canvases. The same component+jsxIndex bookkeeping as canvas-pipeline.ts
|
|
3
|
+
// computes the target ID; tests round-trip an edit through transpileCanvasSource
|
|
4
|
+
// to confirm the IDs line up.
|
|
5
|
+
|
|
6
|
+
import { describe, expect, test } from 'bun:test';
|
|
7
|
+
|
|
8
|
+
import { CanvasEditError, applyEdit, editAttribute } from '../canvas-edit.ts';
|
|
9
|
+
import { transpileCanvasSource } from '../canvas-pipeline.ts';
|
|
10
|
+
|
|
11
|
+
const CANVAS = '/abs/Canvas.tsx';
|
|
12
|
+
|
|
13
|
+
function idsOf(source: string): Record<string, string> {
|
|
14
|
+
// Run the pipeline to learn what data-cd-id each JSX element gets, returning
|
|
15
|
+
// a map keyed by the element-type name so tests can ask "what id is the
|
|
16
|
+
// <span>?" without hardcoding the hash.
|
|
17
|
+
const { withIds } = transpileCanvasSource(CANVAS, source);
|
|
18
|
+
const out: Record<string, string> = {};
|
|
19
|
+
for (const m of withIds.matchAll(/<(\w+)([^>]*?)data-cd-id="([0-9a-f]{8})"/g)) {
|
|
20
|
+
if (!out[m[1] as string]) out[m[1] as string] = m[3] as string;
|
|
21
|
+
}
|
|
22
|
+
return out;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
describe('canvas-edit / applyEdit', () => {
|
|
26
|
+
test('swaps an existing className literal value', () => {
|
|
27
|
+
const src = `function Demo() { return <button className="btn">OK</button>; }`;
|
|
28
|
+
const ids = idsOf(src);
|
|
29
|
+
const id = ids.button as string;
|
|
30
|
+
const out = applyEdit(CANVAS, src, id, 'className', 'btn btn--ghost');
|
|
31
|
+
expect(out.source).toBe(
|
|
32
|
+
`function Demo() { return <button className="btn btn--ghost">OK</button>; }`
|
|
33
|
+
);
|
|
34
|
+
// "btn" (3 chars) → "btn btn--ghost" (14 chars): +11 bytes inside the
|
|
35
|
+
// literal; quotes unchanged.
|
|
36
|
+
expect(out.delta).toBe(11);
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
test('inserts className when missing', () => {
|
|
40
|
+
const src = 'function Demo() { return <div>x</div>; }';
|
|
41
|
+
const ids = idsOf(src);
|
|
42
|
+
const id = ids.div as string;
|
|
43
|
+
const out = applyEdit(CANVAS, src, id, 'className', 'card');
|
|
44
|
+
expect(out.source).toBe(`function Demo() { return <div className="card">x</div>; }`);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('rewrites a plain attribute (aria-label)', () => {
|
|
48
|
+
const src = `function Demo() { return <button aria-label="old">x</button>; }`;
|
|
49
|
+
const ids = idsOf(src);
|
|
50
|
+
const id = ids.button as string;
|
|
51
|
+
const out = applyEdit(CANVAS, src, id, 'aria-label', 'new');
|
|
52
|
+
expect(out.source).toBe(`function Demo() { return <button aria-label="new">x</button>; }`);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
test('swaps a single style.<prop> value inside style={{...}}', () => {
|
|
56
|
+
const src = 'function Demo() { return <div style={{ padding: 8, margin: 4 }}>x</div>; }';
|
|
57
|
+
const ids = idsOf(src);
|
|
58
|
+
const id = ids.div as string;
|
|
59
|
+
const out = applyEdit(CANVAS, src, id, 'style.padding', '14');
|
|
60
|
+
expect(out.source).toBe(
|
|
61
|
+
'function Demo() { return <div style={{ padding: 14, margin: 4 }}>x</div>; }'
|
|
62
|
+
);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('inserts a missing style key into existing style={{...}}', () => {
|
|
66
|
+
const src = 'function Demo() { return <div style={{ padding: 8 }}>x</div>; }';
|
|
67
|
+
const ids = idsOf(src);
|
|
68
|
+
const id = ids.div as string;
|
|
69
|
+
const out = applyEdit(CANVAS, src, id, 'style.color', '"#facc15"');
|
|
70
|
+
// The exact spacing of the appended key is implementation-defined; assert
|
|
71
|
+
// the substring lands without trampling the prior key.
|
|
72
|
+
expect(out.source).toContain('padding: 8');
|
|
73
|
+
expect(out.source).toContain('color: "#facc15"');
|
|
74
|
+
});
|
|
75
|
+
|
|
76
|
+
test('inserts a brand-new style={{}} prop when missing', () => {
|
|
77
|
+
const src = 'function Demo() { return <div>x</div>; }';
|
|
78
|
+
const ids = idsOf(src);
|
|
79
|
+
const id = ids.div as string;
|
|
80
|
+
const out = applyEdit(CANVAS, src, id, 'style.padding', '14');
|
|
81
|
+
expect(out.source).toContain('<div style={{ padding: 14 }}>x</div>');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('throws CanvasEditError when id is not found', () => {
|
|
85
|
+
const src = 'function Demo() { return <div>x</div>; }';
|
|
86
|
+
expect(() => applyEdit(CANVAS, src, 'deadbeef', 'className', 'x')).toThrow(CanvasEditError);
|
|
87
|
+
});
|
|
88
|
+
|
|
89
|
+
test('refuses to edit data-cd-id (owned by pipeline)', () => {
|
|
90
|
+
const src = 'function Demo() { return <div>x</div>; }';
|
|
91
|
+
const ids = idsOf(src);
|
|
92
|
+
const id = ids.div as string;
|
|
93
|
+
expect(() => applyEdit(CANVAS, src, id, 'data-cd-id', 'beefcafe')).toThrow(CanvasEditError);
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
test('throws on malformed source', () => {
|
|
97
|
+
const src = 'function Demo() { return <not closing';
|
|
98
|
+
expect(() => applyEdit(CANVAS, src, 'aaaaaaaa', 'className', 'x')).toThrow();
|
|
99
|
+
});
|
|
100
|
+
|
|
101
|
+
test('targets the right element when multiple share a type', () => {
|
|
102
|
+
const src =
|
|
103
|
+
'function Demo() { return (<section>' +
|
|
104
|
+
`<button className="a">A</button>` +
|
|
105
|
+
`<button className="b">B</button>` +
|
|
106
|
+
'</section>); }';
|
|
107
|
+
// The pipeline visits in pre-order; expose all <button> ids by walking the
|
|
108
|
+
// withIds output and capturing each match.
|
|
109
|
+
const { withIds } = transpileCanvasSource(CANVAS, src);
|
|
110
|
+
const matches = [
|
|
111
|
+
...withIds.matchAll(/<button[^>]*data-cd-id="([0-9a-f]{8})"[^>]*className="([ab])"/g),
|
|
112
|
+
];
|
|
113
|
+
expect(matches.length).toBe(2);
|
|
114
|
+
const idA = matches.find((m) => m[2] === 'a')?.[1] as string;
|
|
115
|
+
expect(idA).toBeDefined();
|
|
116
|
+
const out = applyEdit(CANVAS, src, idA, 'className', 'A-edited');
|
|
117
|
+
expect(out.source).toContain('className="A-edited"');
|
|
118
|
+
expect(out.source).toContain('className="b"');
|
|
119
|
+
});
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
describe('canvas-edit / editAttribute (fs)', () => {
|
|
123
|
+
test('writes the edit atomically + is a no-op when source unchanged', async () => {
|
|
124
|
+
const tmp = `/tmp/canvas-edit-${Math.random().toString(36).slice(2)}.tsx`;
|
|
125
|
+
const src = `function Demo() { return <div className="x">y</div>; }`;
|
|
126
|
+
await Bun.write(tmp, src);
|
|
127
|
+
const ids = idsOf(src);
|
|
128
|
+
const id = ids.div as string;
|
|
129
|
+
|
|
130
|
+
const r1 = await editAttribute(tmp, id, 'className', 'longer-value');
|
|
131
|
+
expect(r1.delta).toBeGreaterThan(0);
|
|
132
|
+
const after = await Bun.file(tmp).text();
|
|
133
|
+
expect(after).toContain('className="longer-value"');
|
|
134
|
+
|
|
135
|
+
// Second pass with the same value — no-op (the source already has it).
|
|
136
|
+
const r2 = await editAttribute(tmp, id, 'className', 'longer-value');
|
|
137
|
+
expect(r2.delta).toBe(0);
|
|
138
|
+
});
|
|
139
|
+
});
|
|
@@ -0,0 +1,127 @@
|
|
|
1
|
+
// canvas-header.ts — Phase 3.6 Task 12a. Tests the JSDoc header projector
|
|
2
|
+
// (idempotent replace + ai_context surfacing).
|
|
3
|
+
|
|
4
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
5
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
6
|
+
import { tmpdir } from 'node:os';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
import {
|
|
10
|
+
type MetaSidecar,
|
|
11
|
+
applyHeader,
|
|
12
|
+
applyHeaderToSource,
|
|
13
|
+
buildHeader,
|
|
14
|
+
} from '../canvas-header.ts';
|
|
15
|
+
|
|
16
|
+
const META: MetaSidecar = {
|
|
17
|
+
title: 'Smoke Canvas',
|
|
18
|
+
subtitle: 'Single hero with CTA — smoke harness',
|
|
19
|
+
brief: 'Verify the JSDoc projection round-trips meta.',
|
|
20
|
+
platform: 'desktop',
|
|
21
|
+
designSystem: 'project',
|
|
22
|
+
opt_out_scope: 'palette',
|
|
23
|
+
css_mode: 'inline',
|
|
24
|
+
sections: [{ id: 's', artboards: [{ id: 'a1' }, { id: 'a2' }] }],
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
describe('canvas-header / buildHeader', () => {
|
|
28
|
+
test('renders all canonical @tags', () => {
|
|
29
|
+
const out = buildHeader({ name: 'Smoke Canvas', meta: META });
|
|
30
|
+
expect(out).toContain('@canvas Smoke Canvas — Single hero with CTA');
|
|
31
|
+
expect(out).toContain('@ds project');
|
|
32
|
+
expect(out).toContain('@platform desktop');
|
|
33
|
+
expect(out).toContain('@opt_out palette');
|
|
34
|
+
expect(out).toContain('@artboards a1 | a2');
|
|
35
|
+
expect(out).toContain('@brief Verify the JSDoc projection');
|
|
36
|
+
expect(out).toContain('@stack React 19 · TSX · Bun.build · css_mode=inline');
|
|
37
|
+
expect(out).toContain('@history .design/_history/smoke-canvas/');
|
|
38
|
+
expect(out).toContain('@handoff bunx shadcn add file://./Smoke Canvas.registry.json');
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
test('surfaces ai_context fields when present', () => {
|
|
42
|
+
const meta: MetaSidecar = {
|
|
43
|
+
...META,
|
|
44
|
+
ai_context: {
|
|
45
|
+
why_this_exists: 'Captures the cold-read contract for future Claude.',
|
|
46
|
+
pinned_decisions: ['Bun.hash over blake3-wasm (smaller dep tree)'],
|
|
47
|
+
known_quirks: ['IDs renumber on sibling-insert — documented in DDR-019'],
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
const out = buildHeader({ name: 'Smoke', meta });
|
|
51
|
+
expect(out).toContain('@notes Captures the cold-read contract');
|
|
52
|
+
expect(out).toContain('@decision Bun.hash over blake3-wasm');
|
|
53
|
+
expect(out).toContain('@quirk IDs renumber on sibling-insert');
|
|
54
|
+
});
|
|
55
|
+
|
|
56
|
+
test('omits ai_context lines when absent', () => {
|
|
57
|
+
const out = buildHeader({ name: 'Smoke', meta: META });
|
|
58
|
+
expect(out).not.toContain('@notes');
|
|
59
|
+
expect(out).not.toContain('@decision');
|
|
60
|
+
expect(out).not.toContain('@quirk');
|
|
61
|
+
});
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe('canvas-header / applyHeaderToSource', () => {
|
|
65
|
+
test('replaces an existing leading block comment', () => {
|
|
66
|
+
const source = `/** STALE HEADER */\nimport { useState } from "react";\nexport default function X() { return <div/>; }\n`;
|
|
67
|
+
const header = '/** NEW HEADER */';
|
|
68
|
+
const out = applyHeaderToSource(source, header);
|
|
69
|
+
expect(out.startsWith('/** NEW HEADER */')).toBe(true);
|
|
70
|
+
expect(out).not.toContain('STALE HEADER');
|
|
71
|
+
expect(out).toContain('import { useState }');
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
test('prepends when no leading block comment exists', () => {
|
|
75
|
+
const source = `import { useState } from "react";\nexport default function X() { return <div/>; }\n`;
|
|
76
|
+
const header = '/** NEW HEADER */';
|
|
77
|
+
const out = applyHeaderToSource(source, header);
|
|
78
|
+
expect(out.startsWith('/** NEW HEADER */')).toBe(true);
|
|
79
|
+
expect(out).toContain('import { useState }');
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('idempotent — applying twice yields the same output', () => {
|
|
83
|
+
const source = `import { useState } from "react";\nexport default function X() { return <div/>; }\n`;
|
|
84
|
+
const header = '/** A */';
|
|
85
|
+
const once = applyHeaderToSource(source, header);
|
|
86
|
+
const twice = applyHeaderToSource(once, header);
|
|
87
|
+
expect(twice).toBe(once);
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
|
|
91
|
+
describe('canvas-header / applyHeader (fs)', () => {
|
|
92
|
+
let dir = '';
|
|
93
|
+
beforeAll(() => {
|
|
94
|
+
dir = mkdtempSync(path.join(tmpdir(), 'header-'));
|
|
95
|
+
});
|
|
96
|
+
afterAll(() => {
|
|
97
|
+
if (dir) rmSync(dir, { recursive: true, force: true });
|
|
98
|
+
});
|
|
99
|
+
|
|
100
|
+
test('writes a fresh header + persists meta projection', async () => {
|
|
101
|
+
const canvas = path.join(dir, 'Demo.tsx');
|
|
102
|
+
const meta = path.join(dir, 'Demo.meta.json');
|
|
103
|
+
await Bun.write(
|
|
104
|
+
canvas,
|
|
105
|
+
'import { useState } from "react";\nexport default function Demo() { return <div/>; }\n'
|
|
106
|
+
);
|
|
107
|
+
await Bun.write(meta, JSON.stringify(META, null, 2));
|
|
108
|
+
const r = await applyHeader(canvas);
|
|
109
|
+
expect(r.changed).toBe(true);
|
|
110
|
+
const out = await Bun.file(canvas).text();
|
|
111
|
+
expect(out.startsWith('/**')).toBe(true);
|
|
112
|
+
expect(out).toContain('@canvas Demo');
|
|
113
|
+
expect(out).toContain('@ds project');
|
|
114
|
+
expect(out).toContain('export default function Demo');
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
test('idempotent — second invocation is no-op', async () => {
|
|
118
|
+
const canvas = path.join(dir, 'Demo2.tsx');
|
|
119
|
+
const meta = path.join(dir, 'Demo2.meta.json');
|
|
120
|
+
await Bun.write(canvas, 'export default function Demo2() { return <div/>; }\n');
|
|
121
|
+
await Bun.write(meta, JSON.stringify(META, null, 2));
|
|
122
|
+
const r1 = await applyHeader(canvas);
|
|
123
|
+
expect(r1.changed).toBe(true);
|
|
124
|
+
const r2 = await applyHeader(canvas);
|
|
125
|
+
expect(r2.changed).toBe(false);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
@@ -0,0 +1,146 @@
|
|
|
1
|
+
// canvas-lib-inline — Phase 3.6.1 Task 9. Inline used canvas-lib exports
|
|
2
|
+
// (+ transitive deps) into a handoff drop.
|
|
3
|
+
|
|
4
|
+
import { describe, expect, test } from 'bun:test';
|
|
5
|
+
|
|
6
|
+
import { type LibMap, buildLibMap, inlineUsedExports } from '../canvas-lib-inline.ts';
|
|
7
|
+
|
|
8
|
+
const LIB_PATH = '/virtual/_lib/canvas-lib.tsx';
|
|
9
|
+
|
|
10
|
+
const SAMPLE_LIB =
|
|
11
|
+
`import type { ReactNode } from "react";\n` +
|
|
12
|
+
'\n' +
|
|
13
|
+
'/** Root wrapper. */\n' +
|
|
14
|
+
'export function DesignCanvas({ children }: { children: ReactNode }) {\n' +
|
|
15
|
+
` return <div className="dc-canvas">{children}</div>;\n` +
|
|
16
|
+
'}\n' +
|
|
17
|
+
'\n' +
|
|
18
|
+
'export function DCArtboard({ children }: { children: ReactNode }) {\n' +
|
|
19
|
+
` return <article className="dc-artboard">{children}</article>;\n` +
|
|
20
|
+
'}\n' +
|
|
21
|
+
'\n' +
|
|
22
|
+
'function internalHelper(name: string): string {\n' +
|
|
23
|
+
' return name.toUpperCase();\n' +
|
|
24
|
+
'}\n' +
|
|
25
|
+
'\n' +
|
|
26
|
+
'export function TokenChip({ name }: { name: string }) {\n' +
|
|
27
|
+
' return <code>{internalHelper(name)}</code>;\n' +
|
|
28
|
+
'}\n' +
|
|
29
|
+
'\n' +
|
|
30
|
+
'export function ColorSwatch({ token }: { token: string }) {\n' +
|
|
31
|
+
' return <div><TokenChip name={token} /></div>;\n' +
|
|
32
|
+
'}\n';
|
|
33
|
+
|
|
34
|
+
function map(): LibMap {
|
|
35
|
+
return buildLibMap(LIB_PATH, SAMPLE_LIB);
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
describe('canvas-lib-inline / buildLibMap', () => {
|
|
39
|
+
test('records every named export', () => {
|
|
40
|
+
const m = map();
|
|
41
|
+
expect(m.has('DesignCanvas')).toBe(true);
|
|
42
|
+
expect(m.has('DCArtboard')).toBe(true);
|
|
43
|
+
expect(m.has('TokenChip')).toBe(true);
|
|
44
|
+
expect(m.has('ColorSwatch')).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
test('also records internal helpers (reachable for transitive resolution)', () => {
|
|
48
|
+
const m = map();
|
|
49
|
+
expect(m.has('internalHelper')).toBe(true);
|
|
50
|
+
});
|
|
51
|
+
|
|
52
|
+
test('captures dep edges (ColorSwatch → TokenChip)', () => {
|
|
53
|
+
const m = map();
|
|
54
|
+
expect(m.get('ColorSwatch')?.deps).toContain('TokenChip');
|
|
55
|
+
});
|
|
56
|
+
|
|
57
|
+
test('captures dep edges (TokenChip → internalHelper)', () => {
|
|
58
|
+
const m = map();
|
|
59
|
+
expect(m.get('TokenChip')?.deps).toContain('internalHelper');
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
test('captures leading JSDoc comment', () => {
|
|
63
|
+
const m = map();
|
|
64
|
+
expect(m.get('DesignCanvas')?.source).toContain('Root wrapper');
|
|
65
|
+
});
|
|
66
|
+
|
|
67
|
+
test('strips leading `export` token from inlined source', () => {
|
|
68
|
+
const m = map();
|
|
69
|
+
expect(m.get('DesignCanvas')?.source).not.toMatch(/^export\s+/);
|
|
70
|
+
});
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
describe('canvas-lib-inline / inlineUsedExports', () => {
|
|
74
|
+
test('no-op when canvas has no @maude/canvas-lib import', () => {
|
|
75
|
+
const canvas = 'export default function X() { return <button>x</button>; }\n';
|
|
76
|
+
const r = inlineUsedExports(canvas, map());
|
|
77
|
+
expect(r.droppedImport).toBe(false);
|
|
78
|
+
expect(r.content).toBe(canvas);
|
|
79
|
+
expect(r.inlined).toEqual([]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
test('inlines a single export + strips import line', () => {
|
|
83
|
+
const canvas =
|
|
84
|
+
`import { DesignCanvas } from "@maude/canvas-lib";\n` +
|
|
85
|
+
'export default function X() { return <DesignCanvas>hi</DesignCanvas>; }\n';
|
|
86
|
+
const r = inlineUsedExports(canvas, map());
|
|
87
|
+
expect(r.droppedImport).toBe(true);
|
|
88
|
+
expect(r.inlined).toEqual(['DesignCanvas']);
|
|
89
|
+
expect(r.content).not.toContain('@maude/canvas-lib');
|
|
90
|
+
expect(r.content).toContain('function DesignCanvas');
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
test('inlines transitive internal deps (ColorSwatch → TokenChip → internalHelper)', () => {
|
|
94
|
+
const canvas =
|
|
95
|
+
`import { ColorSwatch } from "@maude/canvas-lib";\n` +
|
|
96
|
+
`export default function X() { return <ColorSwatch token="--accent" />; }\n`;
|
|
97
|
+
const r = inlineUsedExports(canvas, map());
|
|
98
|
+
expect(r.inlined.sort()).toEqual(['ColorSwatch', 'TokenChip', 'internalHelper']);
|
|
99
|
+
expect(r.content).toContain('function ColorSwatch');
|
|
100
|
+
expect(r.content).toContain('function TokenChip');
|
|
101
|
+
expect(r.content).toContain('function internalHelper');
|
|
102
|
+
});
|
|
103
|
+
|
|
104
|
+
test('handles multi-line import with trailing commas', () => {
|
|
105
|
+
const canvas =
|
|
106
|
+
`import {\n DesignCanvas,\n DCArtboard,\n} from "@maude/canvas-lib";\n` +
|
|
107
|
+
'export default function X() { return <DesignCanvas><DCArtboard /></DesignCanvas>; }\n';
|
|
108
|
+
const r = inlineUsedExports(canvas, map());
|
|
109
|
+
expect(r.inlined.sort()).toEqual(['DCArtboard', 'DesignCanvas']);
|
|
110
|
+
expect(r.content).not.toMatch(/from\s*["']@maude\/canvas-lib["']/);
|
|
111
|
+
});
|
|
112
|
+
|
|
113
|
+
test('throws when canvas imports an unknown export', () => {
|
|
114
|
+
const canvas =
|
|
115
|
+
`import { DoesNotExist } from "@maude/canvas-lib";\n` +
|
|
116
|
+
'export default function X() { return <DoesNotExist />; }\n';
|
|
117
|
+
expect(() => inlineUsedExports(canvas, map())).toThrow(/no such export/);
|
|
118
|
+
});
|
|
119
|
+
|
|
120
|
+
test('inlined content keeps the canvas default export at the top', () => {
|
|
121
|
+
const canvas =
|
|
122
|
+
`import { DesignCanvas } from "@maude/canvas-lib";\n` +
|
|
123
|
+
'export default function X() { return <DesignCanvas>x</DesignCanvas>; }\n';
|
|
124
|
+
const r = inlineUsedExports(canvas, map());
|
|
125
|
+
const defaultIdx = r.content.indexOf('export default');
|
|
126
|
+
const helperIdx = r.content.indexOf('function DesignCanvas');
|
|
127
|
+
expect(defaultIdx).toBeGreaterThan(-1);
|
|
128
|
+
expect(helperIdx).toBeGreaterThan(defaultIdx);
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
test('zero @maude references remain in output', () => {
|
|
132
|
+
const canvas =
|
|
133
|
+
`import { DesignCanvas, ColorSwatch } from "@maude/canvas-lib";\n` +
|
|
134
|
+
`export default function X() { return <DesignCanvas><ColorSwatch token="--x"/></DesignCanvas>; }\n`;
|
|
135
|
+
const r = inlineUsedExports(canvas, map());
|
|
136
|
+
expect(r.content.match(/@maude/g)).toBeNull();
|
|
137
|
+
});
|
|
138
|
+
|
|
139
|
+
test('result is at least as large as the original canvas (helpers added)', () => {
|
|
140
|
+
const canvas =
|
|
141
|
+
`import { DesignCanvas } from "@maude/canvas-lib";\n` +
|
|
142
|
+
'export default function X() { return <DesignCanvas>x</DesignCanvas>; }\n';
|
|
143
|
+
const r = inlineUsedExports(canvas, map());
|
|
144
|
+
expect(r.content.length).toBeGreaterThan(canvas.length);
|
|
145
|
+
});
|
|
146
|
+
});
|
|
@@ -0,0 +1,112 @@
|
|
|
1
|
+
// canvas-lib-resolver — virtual specifier `@maude/canvas-lib` → the dev-server-
|
|
2
|
+
// bundled canvas-lib (Phase 3.6.1 Task 2; relocated to dev-server in Phase
|
|
3
|
+
// 4.0.5 per DDR-025).
|
|
4
|
+
|
|
5
|
+
import { existsSync } from 'node:fs';
|
|
6
|
+
import { mkdir, rm, writeFile } from 'node:fs/promises';
|
|
7
|
+
import path from 'node:path';
|
|
8
|
+
|
|
9
|
+
import { afterAll, beforeAll, describe, expect, test } from 'bun:test';
|
|
10
|
+
|
|
11
|
+
import { buildCanvasModule } from '../canvas-build.ts';
|
|
12
|
+
import {
|
|
13
|
+
CANVAS_LIB_SPECIFIER,
|
|
14
|
+
canvasLibPath,
|
|
15
|
+
readCanvasLibSource,
|
|
16
|
+
} from '../canvas-lib-resolver.ts';
|
|
17
|
+
|
|
18
|
+
const TMP = `/tmp/canvas-lib-resolver-${Math.random().toString(36).slice(2, 8)}`;
|
|
19
|
+
const DESIGN_ROOT = path.join(TMP, 'designRoot');
|
|
20
|
+
|
|
21
|
+
beforeAll(async () => {
|
|
22
|
+
await mkdir(DESIGN_ROOT, { recursive: true });
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
afterAll(async () => {
|
|
26
|
+
await rm(TMP, { recursive: true, force: true });
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('canvas-lib-resolver / canvasLibPath', () => {
|
|
30
|
+
test('returns the dev-server-internal canvas-lib.tsx path', () => {
|
|
31
|
+
const p = canvasLibPath();
|
|
32
|
+
expect(p.endsWith('plugins/design/dev-server/canvas-lib.tsx')).toBe(true);
|
|
33
|
+
expect(existsSync(p)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
test('ignores the legacy designRoot argument (back-compat shim)', () => {
|
|
37
|
+
expect(canvasLibPath('/foo/bar')).toBe(canvasLibPath());
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
test('CANVAS_LIB_SPECIFIER is `@maude/canvas-lib`', () => {
|
|
41
|
+
expect(CANVAS_LIB_SPECIFIER).toBe('@maude/canvas-lib');
|
|
42
|
+
});
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
describe('canvas-lib-resolver / canvasLibResolver plugin', () => {
|
|
46
|
+
test('resolves @maude/canvas-lib through Bun.build', async () => {
|
|
47
|
+
const canvasPath = path.join(DESIGN_ROOT, 'ui', 'Resolves.tsx');
|
|
48
|
+
await mkdir(path.dirname(canvasPath), { recursive: true });
|
|
49
|
+
const canvasSource =
|
|
50
|
+
`import { DesignCanvas, DCArtboard } from "@maude/canvas-lib";\n` +
|
|
51
|
+
'export default function Demo() {\n' +
|
|
52
|
+
' return <DesignCanvas><DCArtboard>hi</DCArtboard></DesignCanvas>;\n' +
|
|
53
|
+
'}\n';
|
|
54
|
+
await writeFile(canvasPath, canvasSource);
|
|
55
|
+
const r = await buildCanvasModule(canvasPath, canvasSource, {
|
|
56
|
+
designRoot: DESIGN_ROOT,
|
|
57
|
+
});
|
|
58
|
+
// The bundled output should contain the resolved lib's function bodies.
|
|
59
|
+
expect(r.js).toContain('dc-canvas');
|
|
60
|
+
expect(r.js).toContain('dc-artboard');
|
|
61
|
+
// The bare specifier should NOT survive as an import.
|
|
62
|
+
expect(r.js).not.toMatch(/from\s*["']@maude\/canvas-lib["']/);
|
|
63
|
+
});
|
|
64
|
+
|
|
65
|
+
test('does not interfere with canvases that never import the lib', async () => {
|
|
66
|
+
const canvasPath = path.join(DESIGN_ROOT, 'ui', 'PlainNoLib.tsx');
|
|
67
|
+
await mkdir(path.dirname(canvasPath), { recursive: true });
|
|
68
|
+
const canvasSource = 'export default function Y() { return <button>hi</button>; }\n';
|
|
69
|
+
await writeFile(canvasPath, canvasSource);
|
|
70
|
+
const r = await buildCanvasModule(canvasPath, canvasSource, {
|
|
71
|
+
designRoot: DESIGN_ROOT,
|
|
72
|
+
});
|
|
73
|
+
expect(r.js).toContain('button');
|
|
74
|
+
});
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
describe('canvas-lib-resolver / readCanvasLibSource', () => {
|
|
78
|
+
test('reads the dev-server-bundled lib source', async () => {
|
|
79
|
+
const src = await readCanvasLibSource();
|
|
80
|
+
// Real bundled lib — assert a stable export name shows up.
|
|
81
|
+
expect(src).toContain('DesignCanvas');
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
test('ignores the legacy designRoot argument', async () => {
|
|
85
|
+
const src = await readCanvasLibSource('/non-existent-design-root');
|
|
86
|
+
expect(src).toContain('DesignCanvas');
|
|
87
|
+
});
|
|
88
|
+
});
|
|
89
|
+
|
|
90
|
+
describe('canvas-lib-resolver / legacy deprecation guard', () => {
|
|
91
|
+
test('legacy <designRoot>/_lib/canvas-lib.tsx is ignored — dev-server bundled lib wins', async () => {
|
|
92
|
+
const projRoot = path.join(TMP, 'legacy-project');
|
|
93
|
+
const legacyLibDir = path.join(projRoot, '_lib');
|
|
94
|
+
await mkdir(legacyLibDir, { recursive: true });
|
|
95
|
+
// Plant a syntactically-bogus legacy file. If the resolver were still
|
|
96
|
+
// reading from `<designRoot>/_lib/canvas-lib.tsx` this would explode at
|
|
97
|
+
// bundle time; with the dev-server-bundled lib it must be a no-op.
|
|
98
|
+
await writeFile(
|
|
99
|
+
path.join(legacyLibDir, 'canvas-lib.tsx'),
|
|
100
|
+
'// LEGACY — should be ignored\nexport const SHOULD_NOT_APPEAR = true;\n'
|
|
101
|
+
);
|
|
102
|
+
const canvasPath = path.join(projRoot, 'ui', 'Legacy.tsx');
|
|
103
|
+
await mkdir(path.dirname(canvasPath), { recursive: true });
|
|
104
|
+
const canvasSource =
|
|
105
|
+
`import { DesignCanvas } from "@maude/canvas-lib";\n` +
|
|
106
|
+
'export default function L() { return <DesignCanvas>hi</DesignCanvas>; }\n';
|
|
107
|
+
await writeFile(canvasPath, canvasSource);
|
|
108
|
+
const r = await buildCanvasModule(canvasPath, canvasSource, { designRoot: projRoot });
|
|
109
|
+
expect(r.js).toContain('dc-canvas');
|
|
110
|
+
expect(r.js).not.toContain('SHOULD_NOT_APPEAR');
|
|
111
|
+
});
|
|
112
|
+
});
|