You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
fix(import): install npm deps from module scripts (CoreBunch#143)
## What changed
- Super Import rewrites known npm ESM CDN imports in module scripts to bare package imports and records matching npm dependency requests.
- Imported script dependencies commit into site.packageJson.dependencies in the same undoable import transaction.
- site_write_code_asset accepts a dependencies map so AI agents can add module-script package imports and package manifest entries in one tool call.
- Site-agent prompt and docs now direct agents to use bare npm imports plus dependencies instead of npm CDN URLs.
## Verification
- bun run lint
- bun run build
- bun test
|`list_code_assets`|`{ type?: 'script' \| 'style' }`|`{ assets }`| List runtime code assets with file ids, paths, full-content hashes, sizes, timestamps, and runtime config |
394
394
|`read_code_asset`|`{ fileId? \| path?, part?, maxChars? }`|`{ fileId, path, type, content, hash, runtime, pageInfo }`| Read an exact script/stylesheet content slice. The `hash` is for the full file; page through with `pageInfo.nextPart`|
395
-
|`write_code_asset`|`{ path, type, content, runtime?}`| asset summary + `{ action}`| Create or replace a runtime script/stylesheet and normalize its runtime config. Existing paths are updated, new paths are created |
395
+
|`write_code_asset`|`{ path, type, content, runtime?, dependencies? }`| asset summary + `{ action, dependencies }`| Create or replace a runtime script/stylesheet and normalize its runtime config. Existing paths are updated, new paths are created. For module scripts, `dependencies` is a package-name → version/range map added to `site.packageJson.dependencies`|
396
396
|`patch_code_asset`|`{ fileId? \| path?, expectedHash, replacements }`| asset summary + `{ replacements }`| Apply exact text replacements only when `expectedHash` matches the latest content. Ambiguous matches require a wider `oldText` or explicit `replaceAll:true`|
397
397
|`inspect_code_runtime`|`{ document?: { type, id } }`|`{ pageId, document, scripts, styles }`| Report which runtime scripts/stylesheets apply to the current page/template or supplied page/template document ref |
398
398
399
399
`insertHtml` / `replaceNodeHtml` intentionally strip `<script>` elements and inline event handlers (`onclick`, `onload`, etc.). When a request needs behavior, the agent should use `write_code_asset({ type: "script", ... })` and then `inspect_code_runtime`, not raw `<script>` tags or event attributes in HTML.
400
400
401
+
Module scripts that need npm packages should import bare package specifiers and declare those packages in the same `write_code_asset` call:
402
+
403
+
```ts
404
+
write_code_asset({
405
+
path: 'src/scripts/motion.js',
406
+
type: 'script',
407
+
content: `import { Motion } from '@motion.page/sdk';`,
408
+
runtime: { format: 'module' },
409
+
dependencies: { '@motion.page/sdk': '1.2.4' },
410
+
})
411
+
```
412
+
413
+
Agents should not use npm CDN URLs such as `esm.sh`, `unpkg`, or jsDelivr for packages that can live in the site dependency manifest.
Copy file name to clipboardExpand all lines: docs/features/html-import.md
+1-1Lines changed: 1 addition & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -142,7 +142,7 @@ Callers splice the fragment into the page tree via `insertImportedNodes(parentId
142
142
|`style="…"` attributes | Declarations harvested onto `node.inlineStyles`; the attribute is removed |
143
143
| HTML comments and processing instructions | Stripped silently — no count |
144
144
145
-
The AI agent should not use stripped constructs for behavior. If an edit needs JavaScript, it writes a real runtime script with `write_code_asset({ type: "script", ... })` and verifies targeting with `inspect_code_runtime` instead of embedding `<script>` or `onclick` in an HTML import.
145
+
The AI agent should not use stripped constructs for behavior. If an edit needs JavaScript, it writes a real runtime script with `write_code_asset({ type: "script", ... })` and verifies targeting with `inspect_code_runtime` instead of embedding `<script>` or `onclick` in an HTML import. Module scripts import npm packages with bare specifiers and declare them in the same `write_code_asset` call's `dependencies` map.
146
146
147
147
After insert, `ImportHtmlModal` builds a toast body from the added-selector count plus the non-zero stripped counts, e.g. `"3 CSS selectors, stripped 2 <script>"`. If nothing notable happened, the toast shows only the node count.
Copy file name to clipboardExpand all lines: docs/features/site-import.md
+17-3Lines changed: 17 additions & 3 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -9,12 +9,12 @@ The static-site pipeline has two parts: a pure analysis function (`buildImportPl
9
9
## TL;DR
10
10
11
11
- Entry: global admin-shell modal, opened from Spotlight or workspace actions. Drop files, a folder, a static `.zip`, or a CMS-exported `.zip` bundle. Static files and CMS bundles both use the four-stage modal (Drop → Review → Conflicts → Import, with completion shown inside the Import stage); Conflicts is skipped when there is nothing to resolve.
12
-
-`buildImportPlan({ fileMap, currentSite, options })` — pure, synchronous — produces an `ImportPlan` with pages, style rules, kept stylesheet files, media, color tokens, custom fonts, Google font install requests, font tokens, and scripts.
12
+
-`buildImportPlan({ fileMap, currentSite, options })` — pure, synchronous — produces an `ImportPlan` with pages, style rules, kept stylesheet files, media, color tokens, custom fonts, Google font install requests, font tokens, scripts, and script npm dependencies inferred from known ESM CDNs.
13
13
-**Per-stylesheet import modes:** each top-level linked stylesheet either converts to editable style rules (default) or imports verbatim as a page-scoped `SiteFile` stylesheet (`options.stylesheetModes`, picked in the Review step). There are no generated scope classes — page isolation comes from the kept file's runtime scope.
14
14
-`commitImportPlan(plan, adapter)` — uploads assets, then wraps all store writes in a single `adapter.commit` call → one Cmd+Z reverts the whole import.
15
15
- Static imports load the current CMS draft into the editor store on demand when launched outside `/admin/site`; if no draft exists, the modal creates an empty site before analysis.
16
16
- Conflict resolution: rename with a numeric suffix (default), overwrite, skip, or custom-rename — per page slug, per class name, per design token (colour / font CSS variable), and per divergent cross-stylesheet class definition, with category-level bulk actions. Token renames rewrite `var(--x)` references so imports stay faithful.
17
-
- What imports: pages, linked CSS plus unconditional local CSS `@import` graphs, `kind:'class'` and `kind:'ambient'` style rules, stylesheets kept as page-scoped files, `@keyframes`, uploadable media/font files, root CSS color tokens, root CSS font tokens, `@font-face` families, known external font stylesheet imports, safe extra HTML attributes on base modules, body-level classes/attributes/style metadata, bare DOM text nodes in mixed content, and executable HTML scripts as page-scoped runtime scripts.
17
+
- What imports: pages, linked CSS plus unconditional local CSS `@import` graphs, `kind:'class'` and `kind:'ambient'` style rules, stylesheets kept as page-scoped files, `@keyframes`, uploadable media/font files, root CSS color tokens, root CSS font tokens, `@font-face` families, known external font stylesheet imports, safe extra HTML attributes on base modules, body-level classes/attributes/style metadata, bare DOM text nodes in mixed content, and executable HTML scripts as page-scoped runtime scripts. Module-script imports from known npm CDNs are rewritten to bare package imports and added to the site dependency manifest.
18
18
- CMS bundle import preserves selected exported tables, rows, optional site shell, media, folders, and redirects using the same merge strategies as site transfer (`replace`, `merge-add`, `merge-overwrite`).
19
19
- HTML forms import through the shared HTML importer as first-class form primitives (`base.form`, controls, labels, submit buttons), not as custom containers.
20
20
- What cannot be modeled: `@layer`, conditional local CSS `@import`, and arbitrary external `@import` — surfaced as warnings when the CSS engine exposes them, never silently dropped.
@@ -35,6 +35,7 @@ src/core/siteImport/
35
35
├── colorTokens.ts — extract root custom-property color tokens from :root/html/body rules
36
36
├── fontTokens.ts — extract root --font-* custom properties as ImportFontToken[] from :root/html/body rules
37
37
├── fontImports.ts — resolve trusted Google CSS2 @import rules into installed-font requests
38
+
├── scriptDependencies.ts — rewrite known npm ESM CDN imports in module scripts into bare package imports + dependency requests
38
39
├── cssImports.ts — expand unconditional local CSS @import graphs while preserving each source path
39
40
├── classCascades.ts — cross-sheet class semantics: detect divergent class definitions as explicit conflicts; apply rename / keep-first / overwrite; enforce one bindable class rule per name
40
41
├── mimeTypes.ts — extension → MIME fallback for FileMap entries that carry no MIME type (e.g. ZIP)
@@ -175,6 +176,19 @@ interface ImportPlan {
175
176
176
177
All URL-shaped values inside `pages[].nodeFragment` props, imported `htmlAttributes` bags, style rule `styles`/`contextStyles`, and supported raw style-rule blocks such as `@keyframes` are normalised to FileMap keys before the plan is returned — `applyAssetRewrites` does exact-string replacement after upload.
177
178
179
+
`ImportScript` entries carry optional npm dependency requests when module-script CDN imports were converted:
|**Color tokens**| CSS custom properties on `:root` / `html` / `body` that look like colours |`extractRootColorTokens` pulls them into `ImportColorToken[]`; they become framework palette tokens. The framework parses hex, rgb/rgba, and hsl/hsla into channels (deriving shades/tints/transparent steps); any other authored value (oklch(), color-mix(), …) still emits its base `--<slug>` verbatim so `var(--x)` references never break. A `--<slug>` that collides with an existing colour token surfaces as a `TokenConflict` (rename / skip / overwrite) |
189
203
|**Fonts**| Self-hosted `@font-face` families with at least one bundled file, plus trusted Google CSS2 imports |`buildFontFamilies` in `assetPlan.ts` picks the best bundled format (woff2 → woff → ttf → otf); `extractGoogleFontImports` turns Google CSS2 `@import` rules into install requests. Commit uploads custom files via `tx.addFonts`, installs Google families through the CMS Google-font installer, then merges those returned `FontEntry` records via `tx.addInstalledFonts`|
190
204
|**Font tokens**| Root `--font-*` variables with font-family stacks |`extractRootFontTokens` pulls them into `ImportFontToken[]`; committed via `tx.addFontTokens` after fonts so matching imported families can be assigned. A `--font-*` that collides with an existing font token surfaces as a `TokenConflict` (rename / skip / overwrite) |
191
-
|**Scripts**| Executable inline scripts and JS files linked by imported HTML | Preserved in source order and committed via `tx.addScripts` with page scope from the source HTML. Classic scripts remain plain `<script>` assets and bypass bundling; `type="module"` scripts keep module semantics. Non-executable script data such as `application/json`, import maps, and templates is skipped. |
205
+
|**Scripts**| Executable inline scripts and JS files linked by imported HTML | Preserved in source order and committed via `tx.addScripts` with page scope from the source HTML. Classic scripts remain plain `<script>` assets and bypass bundling; `type="module"` scripts keep module semantics. Module imports from known npm CDNs (`esm.sh`, `esm.run`, `unpkg`, jsDelivr npm URLs) are rewritten to bare package specifiers and recorded as runtime dependencies, so `https://esm.sh/@motion.page/sdk@1.2.4` becomes `@motion.page/sdk` plus `@motion.page/sdk: 1.2.4` in `packageJson.dependencies`. Non-executable script data such as `application/json`, import maps, and templates is skipped. |
192
206
|**Stylesheets (kept)**| Top-level linked sheets the user opted into `mode: 'file'`| Flattened `@import` graph, Google imports stripped, `url()` normalised; committed via `tx.addStylesheets` as a `SiteFile` (`type: 'style'`) + `site.runtime.styles` entry scoped to the linking pages. Editable afterwards in the Site panel's Styles section and the code editor. |
Copy file name to clipboardExpand all lines: server/ai/tools/site/systemPrompt.ts
+1Lines changed: 1 addition & 0 deletions
Original file line number
Diff line number
Diff line change
@@ -38,6 +38,7 @@ Structure as HTML, styling as CSS:
38
38
Behavior and runtime code:
39
39
- site_insert_html/site_replace_node_html deliberately strip <script> and inline event handlers (onclick/onload/etc). NEVER try to add behavior with <script>, onclick, or custom inline JS in HTML.
40
40
- To add behavior such as theme toggles, tabs, menus, filters, or DOM-ready interactions, use site_write_code_asset({ type:"script", path:"src/scripts/...", content, runtime }). The script file is stored in the site file layer and loaded through site.runtime.
41
+
- For npm packages in module scripts, import bare package specifiers (e.g. import { Motion } from "@motion.page/sdk") and include dependencies: { "@motion.page/sdk": "1.2.4" } in the SAME site_write_code_asset call. Do not use npm CDN URLs like esm.sh/unpkg/jsDelivr for packages that belong in the site dependency manifest.
41
42
- Before changing existing scripts or user stylesheets, call site_list_code_assets/site_read_code_asset. Patch exact spans with site_patch_code_asset using the latest hash; if the text occurs multiple times, use a larger oldText span or replaceAll:true intentionally.
42
43
- Use site_inspect_code_runtime after writing code to confirm scripts/styles apply to the current page/template, are enabled, and have the intended priority/placement/timing.
'Create or replace a runtime script/style file in site.files and attach normalized site.runtime config. Use `type:"script"` for behavior such as theme toggles, menus, tabs, analytics hooks, and DOM-ready interactions; use `type:"style"` for global user stylesheets that should load as files. `path` is a safe site-relative path such as src/scripts/theme-toggle.js or src/styles/theme.css. `runtime` is optional and merges with existing/default config.',
248
+
'Create or replace a runtime script/style file in site.files and attach normalized site.runtime config. Use `type:"script"` for behavior such as theme toggles, menus, tabs, analytics hooks, and DOM-ready interactions; use `type:"style"` for global user stylesheets that should load as files. `path` is a safe site-relative path such as src/scripts/theme-toggle.js or src/styles/theme.css. `runtime` is optional and merges with existing/default config. For module scripts that import npm packages, use bare package imports in `content` and declare them in `dependencies` (package name → semver/range) so they are added to the site dependency manifest; do not use npm CDN URLs for npm packages.',
0 commit comments