create-bascik 1.0.0-rc.2 → 1.0.0-rc.4

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/assets/SKILL.md CHANGED
@@ -286,7 +286,7 @@ Scoped CSS can live in a paired `.css` file or one or more inline `<style>` tags
286
286
  At build time, Bascik extracts all inline `<style>` blocks, combines them with any companion `.css` file, scopes them, and injects them into the document `<head>`. Using multiple `<style>` tags (or mixing them with a companion `.css` file) is supported but not recommended for readability and maintainability. Choose a single stylesheet pattern per component.
287
287
 
288
288
  ### Global Class Passthrough
289
- Bascik checks whether a class name in a component template is defined in the component's CSS (paired `.css` or inline `<style>`). Only class names defined in the component's stylesheet are scoped (prefixed/hashed). Classes in component HTML that are NOT defined in the component's stylesheet (such as global utility classes like `skip-link`, `flex`, `hidden`, or design system classes) pass through as unscoped global classes so global stylesheets continue to match them.
289
+ Bascik checks whether a class name in a component template is defined in the component's CSS (paired `.css` or inline `<style>`). Only class names defined in the component's stylesheet are scoped (prefixed/hashed). Classes in component HTML that are NOT defined in the component's stylesheet (such as global utility classes like `skip-link`, `flex`, `hidden`, or design system classes) pass through as unscoped global classes so global stylesheets continue to match them. This includes a component with no stylesheet at all, such as a header or footer styled entirely by a global stylesheet: none of its classes are scoped, so it needs no `.css` file or placeholder comment. It also means such a component has no class isolation, so a script querying `.item` in it matches every `.item` on the page; define the class in a stylesheet to scope it.
290
290
 
291
291
  Pair a `.css` file alongside the HTML in a same-named directory:
292
292
 
@@ -406,7 +406,7 @@ Define your design tokens once in a global stylesheet, then consume them inside
406
406
  * `@import`: local file imports (`@import "./file.css"`) are inlined recursively and scoped to the component; remote URLs (`@import "https://..."`) are preserved and hoisted to the top of the compiled stylesheet
407
407
  * Standalone attribute selectors (e.g. `[data-state]`): not scoped and can leak globally; anchor with a scoped class: `.card[data-state]`
408
408
  * `[id]` selectors: `[id]` and `[id="..."]` attribute selectors in CSS are stripped at compile time because they cannot be scoped without DOM wrapping
409
- * Compound element selectors: `.class element {}` and `.class > element {}` are scoped (element converted to class and injected on matching HTML elements); patterns with two bare elements (`div p {}`) still require a class anchor on the left
409
+ * Compound element selectors: every element name in a chain is converted to a scoped class and injected on matching elements in the component's own template, with or without a class anchor (`.card p {}`, `div p {}`, `p + p {}`, `nav a {}`, `ul li * {}` all work); they never reach page markup or a child component's root
410
410
  * Element names inside `:is()`, `:where()`, and `:has()` are not converted; use class selectors inside those pseudo-classes instead
411
411
 
412
412
  ---
@@ -737,12 +737,28 @@ If a component template contains multiple root elements, inherited attributes ar
737
737
 
738
738
  Inherited class names are not scoped, they are treated as global page-level classes. To disable inheritance, set `scoping.inheritAttributes` to `false` in `bascik.config.ts`.
739
739
 
740
+ ### Styling a Child Component from Its Parent
741
+ A component's CSS applies only to markup written in its own template. A parent's element selector such as `nav a { }` never matches the root element of a child component, so components stay isolated. To style a child's root from the parent, put a class on the child's usage tag and define that class in the parent's CSS. Bascik scopes it to the parent and merges it onto the child root. A class not defined in the parent's CSS passes through unscoped.
742
+
743
+ ```html
744
+ <!-- illustrative: site-nav.html (parent) -->
745
+ <nav>
746
+ <nav-link class="item" href="/">Home</nav-link>
747
+ </nav>
748
+ <!-- site-nav.css: .item { padding: 1em 0.5em; } -->
749
+ <!-- nav-link.html (child, root is an anchor): <a><span data-bascik-slot></span></a> -->
750
+ <!-- output: <a class="bascik__site-nav__item" href="/">Home</a> -->
751
+ ```
752
+
753
+ The same applies to markup that a build script or slot produces: put the class on each child usage tag.
754
+
740
755
  ### Internal Masking vs. Preserving Element Contents
741
756
 
742
757
  These two mechanisms serve distinct purposes:
743
758
 
744
759
  * **Internal scanning mask (internal, hardcoded, not configurable):** Bascik temporarily blanks the contents of `<script>`, `<style>`, `<textarea>`, and HTML comments while scanning with regular expressions, so a component tag inside a JavaScript string, style block, or comment is not mistaken for real markup. The mask is discarded immediately after scanning. Authors never interact with or configure this behavior.
745
- * **Preserve scoping (authoring choice):** Keep scoping enabled by default. Use `scoping.preserve` (default `['code']`) when every matching tag and subtree must keep literal `id`, `name`, and `class` values. Entries are exact tag names or `*` wildcard patterns (for example `vendor-*` preserves every tag whose name starts with `vendor-`); both use the same semantics. Use bare `data-bascik-preserve` for one element and subtree, or list selected attributes such as `data-bascik-preserve="name"`. Nesting only widens. Preserving `name` gives up per-instance radio-group isolation, so reserve it for external form endpoints that require literal field names.
760
+ * **Preserve scoping (authoring choice):** Keep scoping enabled by default. Use `scoping.preserve` (default `['code']`) when every matching tag and subtree must keep literal `id`, `name`, and `class` values. Entries are exact tag names or `*` wildcard patterns (for example `vendor-*` preserves every tag whose name starts with `vendor-`); both use the same semantics. Use bare `data-bascik-preserve` for one element and subtree, or list selected attributes such as `data-bascik-preserve="name"`. Nesting only widens. Preserving `name` gives up per-instance radio-group isolation, so reserve it for external form endpoints that require literal field names. Preserving an `id` also stops reference rewriting inside the preserved region: a link inside a preserved subtree stays literal even if its target is scoped, so a link and its target must be both preserved or both scoped (a scoped link to a preserved target works). Never preserve a skip link to "keep it working"; put the target `id` on the page shell. Preserve does not silence unresolved-tag warnings.
761
+ * **External custom elements (`components.external`):** Hyphenated tags that belong to a browser custom element or library (for example `heading-anchors`) print `Unresolved component tag` on every page and appear in `bascik --check`. List them in `components: { external: ['heading-anchors', 'vendor-*'] }` (exact names or `*` wildcards, case-insensitive) to remove both. It changes diagnostics only: a real component of the same name still expands, scoping is unaffected, and typos stay reported. To use an npm custom element, copy its ready-to-use module into `dist/` with a `phase: 'pre'` exec script (files in `node_modules` are never copied, and bare specifiers in client scripts are not rewritten), load it with `<script type="module" src="/assets/vendor/name.js">`, and declare the tag here. Bundle it with esbuild instead if it imports other packages.
746
762
 
747
763
  ### Self-Closing Tags
748
764
  Components that do not contain inner slot content should always use self-closing void syntax:
@@ -771,25 +787,28 @@ Components work inside `<head>` to organize metadata and shared links:
771
787
 
772
788
  ## 8. Build-time Scripts
773
789
 
774
- `<script data-bascik-build>` blocks are executed at transpile time as Node.js ESM modules. The script's stdout is injected in place of the tag. Runs in both dev and build modes.
790
+ `<script data-bascik-build>` blocks are executed at transpile time as Node.js ESM modules. The script exports a default callable function whose returned HTML string replaces the tag. Runs in both dev and build modes.
775
791
 
776
792
  ```html
777
793
  <script data-bascik-build>
778
794
  import { readFile } from 'node:fs/promises';
779
795
  import { marked } from 'marked';
780
- const md = await readFile('./content/intro.md', 'utf8');
781
- console.log(marked(md));
796
+
797
+ export default async function () {
798
+ const md = await readFile('./content/intro.md', 'utf8');
799
+ return marked(md);
800
+ }
782
801
  </script>
783
802
  ```
784
803
 
785
- * Top-level `import` and top-level `await` are supported.
804
+ * Top-level `import` and top-level `await` are supported at module scope.
786
805
  * CWD is the project root. Quoted data paths (`readFile('./content/x.md')`) resolve from there.
787
806
  * **Importing shared helpers: use `@/` by default.** `import { renderMd } from '@/lib/md-renderer.ts'` resolves against `scripts.importRoot` (default `src`), so the identical import line works from any page or component at any nesting depth. Relative `./` and `../` specifiers still work and resolve against the file that contains the script; use them for helpers that live next to the page or component. Both forms also apply to `data-bascik-server`, `data-bascik-routes`, and the `src="…"` attribute on those tags.
788
807
  * **Never use a bare leading `/`.** `import x from '/lib/x.ts'` or `src="/lib/x.ts"` inside a Bascik script is a hard compile error (and a red diagnostic in the VS Code extension), regardless of `onBuildScriptError`. A bare slash is ambiguous between filesystem root and site root. The error names the two valid rewrites: `@/lib/x.ts` (import root) or `./lib/x.ts` (relative). Plain client `<script>` tags are unaffected.
789
808
  * **Alias gotchas:** only the exact `@/` prefix is an alias (`@scope/pkg` is a normal package). Aliases are rewritten only inside script blocks; a helper file importing another helper must use `./` or `../`. Add shared build helpers to `pipeline.watchPaths` when outside the pages/components directories. The import-root watcher only invalidates request-time modules, not page compilation caches.
790
- * Use `console.log()` or `process.stdout.write()` to output HTML. Build scripts execute as top-level Node.js ESM modules rather than wrapped functions, so top-level `return` is a JavaScript syntax error; output is sent to stdout instead.
809
+ * Define a callable default export function (`export default async function () { return ...; }`) that returns an HTML string. Standard console methods (`console.log()`, `console.error()`) log to the terminal for debugging only and never become generated markup.
791
810
  * Build scripts run before component resolution, so their output can contain component tags.
792
- * Each uncached build script executes in its own fresh child process, isolated from every other script, regardless of how many siblings are cache misses. Output is assembled in document order once all scripts complete. Bounded concurrency is enforced by a memory-aware semaphore, but scripts never share a process or global ESM registry, so detached async output from one script can never bleed into a neighbor.
811
+ * Each uncached build script executes in its own fresh child process, isolated from every other script, regardless of how many siblings are cache misses. Results travel across a dedicated result transport. Bounded concurrency is enforced by a memory-aware semaphore, but scripts never share a process or global ESM registry, so detached async output from one script can never bleed into a neighbor.
793
812
  * On error, behavior is controlled by three script-specific options in `bascik.config.ts`: `scripts.onBuildScriptError`, `scripts.onRoutesScriptError`, and `scripts.onServerScriptError` (each supports `'warn'`, `'error'`, or `'ignore'`). Defaults are mode-aware: `'warn'` in dev, `'error'` during `--build` and `--server`. For `data-bascik-stream` scripts, an error cannot produce an HTTP 500 because headers are already committed; the slot is emitted empty, the failure is logged at the configured severity, and the document completes.
794
813
  * **Stack Trace Remapping:** For `<script data-bascik-build>`, `<script data-bascik-server>`, and `<script data-bascik-stream>` blocks, Bascik automatically intercepts child-process stack traces, filters out noisy Node.js internal files, stack frames, and `Command failed:` headers, and remaps temporary execution files back to your source HTML file and line offset (e.g., `src/pages/dashboard.html:25`). This filters out the noise of internal V8 loader frames and child process execution headers, leaving only the clean, actionable stack trace of your template and helper scripts. In VS Code or terminal emulators, you can Cmd+Click (or Ctrl+Click) the file reference in the error log to jump directly to the failing script's exact line.
795
814
  * **Hard error:** combining any of `data-bascik-build`, `data-bascik-routes`, `data-bascik-server`, or `data-bascik-stream` on the same tag throws and aborts the build. Directives are mutually exclusive.
@@ -812,6 +831,10 @@ Build scripts receive these `process.env` variables:
812
831
 
813
832
  These are critical for scripts that generate per-page output. A script using `BASCIK_SOURCE_FILE`, `BASCIK_PAGE_FILE`, or `BASCIK_PAGE_PATH` gets a separate cache entry per page automatically.
814
833
 
834
+ `BASCIK_ROUTE` may be any size: over 32 KB it is delivered through a temporary file and set before the script runs, so reading `process.env.BASCIK_ROUTE` is unchanged.
835
+
836
+ **Printed directives never run.** Build script output is transpiled again (component tags expand, client scripts run in the browser), but any `data-bascik-build`, `data-bascik-server`, `data-bascik-stream`, or `data-bascik-routes` script in the output is removed with a warning. Write directives only in source files. This is not a sanitizer: HTML from a CMS or API must still go through an allowlist before printing, because its ordinary scripts, handlers, and component tags are kept.
837
+
815
838
  ### The Fetch-Once Pattern
816
839
 
817
840
  When a data-driven page needs the same data in several places, read or fetch it **once at page level** in a single `data-bascik-build` script, then apply it everywhere on the page. Not once per component instance. The same data object can feed a second script, a JSON payload for client JavaScript, or a component prop, without a second network round trip or file read.
@@ -867,15 +890,17 @@ With `pipeline.workers: true`, multiple workers share the same cache directory.
867
890
 
868
891
  ### Rendering and Styling Markdown
869
892
 
870
- Install a Markdown parser such as `marked`, read the source in a build script, and write the resulting HTML to stdout:
893
+ Install a Markdown parser such as `marked`, read the source in a build script, and return the resulting HTML from the default export:
871
894
 
872
895
  ```html
873
896
  <script data-bascik-build>
874
897
  import { readFile } from 'node:fs/promises';
875
898
  import { marked } from 'marked';
876
899
 
877
- const md = await readFile('./content/article.md', 'utf8');
878
- console.log(marked(md));
900
+ export default async function () {
901
+ const md = await readFile('./content/article.md', 'utf8');
902
+ return marked(md);
903
+ }
879
904
  </script>
880
905
  ```
881
906
 
@@ -901,14 +926,14 @@ Markdown stays comfortable for authors, while the published page stays **vanilla
901
926
 
902
927
  The parser emits ordinary HTML, so it can be styled by a global stylesheet or wrapped in a Bascik component. For reusable scoped styles, have the script emit `<markdown-content>${marked(md)}</markdown-content>` and give that component a default slot.
903
928
 
904
- Use wrapper descendant selectors for generated slot content:
929
+ Use the wrapper class plus `:is()` around element names for generated slot content:
905
930
 
906
931
  ```css
907
- .markdown-content h2 { margin-block: 2.5rem 0.75rem; }
908
- .markdown-content blockquote { border-left: 4px solid currentColor; }
932
+ .markdown-content :is(h2) { margin-block: 2.5rem 0.75rem; }
933
+ .markdown-content :is(blockquote) { border-left: 4px solid currentColor; }
909
934
  ```
910
935
 
911
- Do not rely on a bare `h2 {}` component rule for Markdown passed through a slot. Bare element rules are transformed before slot content is inserted; a scoped wrapper selector continues to match the generated descendants.
936
+ Do not write `h2 {}` or `.markdown-content h2 {}` for Markdown passed through a slot. Bascik scopes an element name by adding a class to the matching elements in the component's own template, also after a wrapper class, so slotted headings (plain tags) never match. Names inside `:is()` are left as written; the wrapper class stays scoped and specificity is unchanged.
912
937
 
913
938
  ### Page-Aware Scripts
914
939
 
@@ -943,8 +968,8 @@ Use it from any page's `<head>`:
943
968
  ```html
944
969
  <head>
945
970
  <script data-bascik-build>
946
- import { canonical } from '../../scripts/canonical.ts';
947
- console.log(await canonical());
971
+ import { canonical } from '@/lib/canonical.ts';
972
+ export default async () => await canonical();
948
973
  </script>
949
974
  </head>
950
975
  ```
@@ -996,25 +1021,28 @@ Dynamic routes allow you to generate multiple static HTML files from a single te
996
1021
  ```html
997
1022
  <!-- src/pages/blog/[slug].html -->
998
1023
  <script data-bascik-routes>
999
- const posts = [
1000
- { slug: 'hello-world', title: 'Hello World' },
1001
- { slug: 'second-post', title: 'Second Post' }
1002
- ];
1003
- const routes = posts.map(p => ({
1004
- params: { slug: p.slug },
1005
- data: p
1006
- }));
1007
- console.log(JSON.stringify(routes));
1024
+ export default async function () {
1025
+ const posts = [
1026
+ { slug: 'hello-world', title: 'Hello World' },
1027
+ { slug: 'second-post', title: 'Second Post' }
1028
+ ];
1029
+ return posts.map(p => ({
1030
+ params: { slug: p.slug },
1031
+ data: p
1032
+ }));
1033
+ }
1008
1034
  </script>
1009
1035
 
1010
1036
  <script data-bascik-build>
1011
- const { params, data } = JSON.parse(process.env.BASCIK_ROUTE || '{}');
1012
- console.log(`<h1>${data.title}</h1>`);
1037
+ export default function () {
1038
+ const { params, data } = JSON.parse(process.env.BASCIK_ROUTE || '{}');
1039
+ return `<h1>${data.title}</h1>`;
1040
+ }
1013
1041
  </script>
1014
1042
  ```
1015
1043
 
1016
1044
  Rules:
1017
- * Script must output a valid JSON array of route objects with required `params` (matching all bracket names in the template path) and optional `data`.
1045
+ * Script must export a callable default function returning an array of route objects with required `params` (matching all bracket names in the template path) and optional `data`. To generate zero routes, return `[]`.
1018
1046
  * Route parameters must be URL-safe tokens; characters like `#`, `%`, `&`, `'`, `+`, spaces, leading dots, and Windows device names are disallowed.
1019
1047
  * In build scripts, `process.env.BASCIK_ROUTE` provides the current `{ params, data }` payload.
1020
1048
  * Dynamic route templates are expanded into concrete static HTML files during `bascik --build` and dev server startup. Route collisions with static pages or other templates cause build errors.
@@ -1157,6 +1185,9 @@ export default defineConfig({
1157
1185
  out: 'dist',
1158
1186
  api: 'src/api',
1159
1187
  },
1188
+ components: {
1189
+ external: [],
1190
+ },
1160
1191
  scoping: {
1161
1192
  scriptBlocks: true,
1162
1193
  inheritAttributes: true,
@@ -1189,6 +1220,7 @@ export default defineConfig({
1189
1220
  watchPaths: [],
1190
1221
  exec: [],
1191
1222
  workers: false,
1223
+ onExecError: 'warn', // 'warn' | 'error'. Default: 'warn' in dev, 'error' for --build
1192
1224
  },
1193
1225
  scripts: {
1194
1226
  cache: { enabled: true },
@@ -1246,6 +1278,7 @@ When creating or modifying `bascik.config.ts`:
1246
1278
  * **Do NOT invent non-existent `pipeline.exec` scripts:** `pipeline.exec` is only for executing existing custom lifecycle script files. If no custom script file exists in the workspace, omit it.
1247
1279
  * **Array Replacement in `build`:** Array properties like `pipeline.exec`, `pipeline.watchPaths`, and `assets.inlineStyles` are replaced as atomic values (not concatenated) when specified in `export const build`. When defining `build.pipeline.exec`, include all scripts that should run in production builds.
1248
1280
  * **Write artifacts to `dist/`:** Lifecycle scripts must write generated artifacts only to the output directory, never source files or watched paths. Never configure `pipeline.watchPaths` or `pipeline.exec[].watch` for generated outputs. There is no `outputs` option. Exec receives `BASCIK_BUILD`, `BASCIK_PAGES_DIR`, `BASCIK_OUT_DIR`, `BASCIK_BASE`, and `BASCIK_SITE_URL`. Use `pre` for required generated dependencies, not `parallel`.
1281
+ * **Exec failures (`pipeline.onExecError`):** `'warn'` reports a failing script (terminal and browser overlay) and keeps going; `'error'` stops and exits 1. Applies to pre, parallel, and post, at startup and on edits. Default is `'warn'` for the dev server (a typo must not end the session) and `'error'` for `--build` (a failed script must never produce a build that looks successful). Set it explicitly to flip either mode. Page compile errors are unaffected.
1249
1282
  * **Source-owned exec phases:** `pipeline.exec[].watch` selects matching scripts after a source edit. One cycle coalesces overlap with pages, components, and `pipeline.watchPaths`: pre finishes before compilation, parallel starts alongside it, post starts after compilation and disk writes. Known source dependents rebuild once; unknown external dependencies conservatively rebuild all pages without rerunning unmatched scripts. Exec-only inputs need no duplicate `pipeline.watchPaths`. Completion never queues another compile or reload. Edits during work are retained; failed paths retry on the next source edit. Pre/compile/post failures discard success reloads. Dev parallel stays nonblocking and reports every failure; build joins parallel and compilation/post before success. Never suppress watched script edits as presumed self-writes. Bascik cannot safely identify source-writing loops. `scripts.importRoot` and `assets.inlineStyles` do not add compilation watches.
1250
1283
  * **Stick to recommended defaults without restating them:** Preserve `scoping.deduplicateCss: true`, `scoping.scriptBlocks: true`, and `scoping.inheritAttributes: true` unless specifically instructed otherwise or integrating global utility frameworks like Tailwind CSS. Do not write these values into a config just to preserve them.
1251
1284
  * **Set `BASCIK_SITE_URL` for production features:** Provide the site URL via the environment (e.g. `BASCIK_SITE_URL=https://example.com bascik --build`) when page-aware canonical scripts, sitemaps, or `robots.txt` generation are enabled. Never put `siteUrl` in `bascik.config.ts`.
@@ -1457,7 +1490,7 @@ While the dev server is active, Bascik watches your file system and incrementall
1457
1490
  #### 3. Transpilation & Build Errors
1458
1491
  Development and production builds handle page failures differently. The dev server logs a failed page, completes boot, continues serving healthy pages, and retries the failed page on the next save. A production `bascik --build` waits for every page job, reports all failures together, and exits nonzero rather than reporting success with missing output.
1459
1492
 
1460
- Hard build failures include a missing or unreadable configured pages directory, a page without a non-empty `<body>`, runaway component expansion, output directory creation failure, and page write failure. `ENOENT` write errors are not ignored.
1493
+ Hard build failures include a missing or unreadable configured pages directory, a missing or unreadable components root listed in `directory.components`, a page without a non-empty `<body>`, runaway component expansion, output directory creation failure, and page write failure. `ENOENT` write errors are not ignored. A missing default `src/components/` is not an error: the project has no components until the directory is created.
1461
1494
 
1462
1495
  ```terminal
1463
1496
  Build failed with 2 page errors:
@@ -1482,7 +1515,7 @@ Build failed with 2 page errors:
1482
1515
  ```terminal
1483
1516
  [bascik] Unresolved component tag in "pages/about.html": <my-mistyped> - no matching component file found. Run `bascik --check` for a full report.
1484
1517
  ```
1485
- Use `bascik --check` (or `bascik --check --strict`) as the CI gate for project references and diagnostics.
1518
+ Use `bascik --check` (or `bascik --check --strict`) as the CI gate for project references and diagnostics. Tags that belong to a custom element or library can be declared in `components.external` so neither the warning nor `--check` reports them.
1486
1519
 
1487
1520
  #### 4. Static Analysis (`bascik --check`)
1488
1521
  Run `bascik --check` from your project root to validate pages, component files, config, and API route files without starting the dev server or writing any output:
@@ -1551,7 +1584,7 @@ What to check in compiled output:
1551
1584
  * **Component resolution:** every custom tag (e.g. `<site-nav>`) should be replaced with expanded HTML. A hyphenated tag still present in `dist/` means no component file matched.
1552
1585
  * **Scoped class names:** attributes like `class="bascik__site-nav__nav"` (or a short hash with `minify.identifiers`) confirm CSS scoping ran correctly.
1553
1586
  * **Injected `<style>` block:** the `<head>` should contain one combined `<style>` with CSS from all components used on that page.
1554
- * **Build script output:** `<script data-bascik-build>` is replaced with stdout; if missing, check the terminal for a `[bascik] build script error` line.
1587
+ * **Build script output:** `<script data-bascik-build>` is replaced with the returned HTML string; if missing, check the terminal for a `[bascik] build script error` line.
1555
1588
  * **Server script output:** `<script data-bascik-server>` is replaced at request time; if output is missing on a live request, check the terminal for a `[bascik] server script error` line. Remember these scripts run in Node.js, not the browser, they require `bascik --server` or the dev server to execute.
1556
1589
  * **Slot and prop values:** verify fallback and injected text appear in the right place.
1557
1590
 
@@ -1827,7 +1860,7 @@ async function walk(dir: string, ext: string): Promise<string[]> {
1827
1860
  return out;
1828
1861
  }
1829
1862
 
1830
- describe('dist HTML — WHATWG spec compliance', () => {
1863
+ describe('dist HTML: WHATWG spec compliance', () => {
1831
1864
  it('every page is free of parse errors', async () => {
1832
1865
  const files = await walk(DIST_DIR, '.html');
1833
1866
  const failures: string[] = [];
@@ -1844,7 +1877,7 @@ describe('dist HTML — WHATWG spec compliance', () => {
1844
1877
  });
1845
1878
  ```
1846
1879
 
1847
- Run after `bascik --build`. Catches corrupted script bodies, mismatched tags, and unreplaced internal tokens that string-based assertions miss. Bascik is a build tool — hold output to the full spec, not browser recovery behavior.
1880
+ Run after `bascik --build`. Catches corrupted script bodies, mismatched tags, and unreplaced internal tokens that string-based assertions miss. Bascik is a build tool, so hold output to the full spec, not browser recovery behavior.
1848
1881
 
1849
1882
  ### Testing Site Logic in a Bascik Project
1850
1883
 
@@ -1949,8 +1982,66 @@ Detailed per-framework migration guides live at `/switch/*`. Key patterns that a
1949
1982
  - **Slots:** `<slot />` / `children` → `data-bascik-slot` (no value) for default, `data-bascik-slot="name"` for named slots.
1950
1983
  - **Props:** `defineProps` / component props → `data-bascik-prop-*` attributes (text only).
1951
1984
  - **Reactive state:** `ref`, `useState`, etc. → plain `<script>` with vanilla JS. Bascik scopes `id` values so multiple instances stay independent.
1952
- - **Routing:** Client-side router → one `.html` file per URL in `src/pages/`. No dynamic segments; generate static files for parameterized routes.
1953
- - **Build-time data:** `onMounted` / `getStaticProps` / frontmatter → `<script data-bascik-build>` (Node.js ESM, stdout injected).
1985
+ - **Routing:** Client-side router → one `.html` file per URL in `src/pages/`. For parameterized routes (`[slug].astro`, `getStaticPaths`, Eleventy pagination), use a dynamic route template such as `src/pages/blog/[slug]/index.html` (trailing-slash URLs) with a `<script data-bascik-routes>` block. Do not generate page files into `src/pages/`. A route param cannot contain `/`; use one template per depth.
1986
+ - **Build-time data:** `onMounted` / `getStaticProps` / frontmatter → `<script data-bascik-build>` (Node.js ESM, returned HTML string replaces the tag).
1987
+ - **Content collections (Astro `getCollection`, Eleventy `collections`):** No built-in equivalent. Write a `src/lib/posts.ts` helper that reads `content/` with `fs`, parses front matter with `gray-matter`, validates with `zod`, and sorts by the parsed date (not the file name). Escape every interpolated value. Drafts: skip them when `process.env.BASCIK_BUILD === '1'`. Exclude scripts that read `content/` from `scripts.cache` and add `content/` to `pipeline.watchPaths`.
1988
+ - **Template logic (Nunjucks/Liquid loops, filters, includes with variables):** Becomes TypeScript helpers in `src/lib/` that return HTML. Only static includes become components. Pages keep `<html>`/`<head>`/`<body>`; a component cannot own them.
1989
+ - **Feeds, sitemaps, images:** RSS/Atom is a `pipeline.exec` script (`phase: 'post'`) writing to `BASCIK_OUT_DIR`. Sitemap and robots are built in. No image optimization or syntax highlighting built in; do it in your own build step.
1990
+ - **Component scripts that print styled markup:** Output of a component build script, page-aware or not, is scoped like the template, including markup returned by imported helpers. Classes the component stylesheet does not define stay global.
1991
+
1992
+ ### From React
1993
+
1994
+ Full guide: `/switch/from-react`. Key React-specific mappings:
1995
+
1996
+ | React | Bascik |
1997
+ |-------|--------|
1998
+ | Function component | `.html` file in `src/components/<name>/`, folder name is the hyphenated tag |
1999
+ | `children` | `data-bascik-slot` (no value), fallback inside it |
2000
+ | Render prop / named children | `<div data-bascik-slot="x">` wrapper at the usage site and in the component |
2001
+ | `props.x` | `data-bascik-prop-x` (text only; booleans and numbers arrive as strings) |
2002
+ | Prop used as an attribute | `data-bascik-attr-<attribute>="propName"` |
2003
+ | Object, array, or function prop | Not possible. Print one component per item from a build script; use a bubbling `CustomEvent` instead of a callback |
2004
+ | `useState` in the common parent | Variables in the parent component's `<script>`; a function applies them to the DOM |
2005
+ | `onChange` callback prop | Child dispatches `new CustomEvent('x', { bubbles: true, detail })` from its own element; the parent listens on its root |
2006
+ | Re-render of a filtered list | One function sets `hidden` on existing rows; there is no diff, so write every place a value shows |
2007
+ | `.map()` over data | `<script data-bascik-build>` that prints component tags with escaped props |
2008
+ | Conditional style | A `data-` attribute and a CSS selector, in the component's own CSS |
2009
+ | React Router `/blog/:slug` | `src/pages/blog/[slug]/index.html` with `<script data-bascik-routes>` |
2010
+
2011
+ React-specific gotchas:
2012
+
2013
+ - **Escape every interpolated value in a build script.** JSX does it for you; a template string does not.
2014
+ - **Forwarding `children` to an inner component:** write a default slot marker between the inner component's tags, `<inner-box><div data-bascik-slot>fallback</div></inner-box>`. It receives the outer usage's default content. Named wrappers between the inner tags fill the inner component's named slots.
2015
+ - **Plain inputs can disagree with state after Back or reload.** Report the fields again on `pageshow`. A form with one text box submits on Enter; call `preventDefault()` in a `submit` listener when a script drives the form.
2016
+ - **Component scripts run once and are moved in production.** Minified production HTML moves them to the end of the document; the dev server leaves them in place. Look up elements inside handlers or after `DOMContentLoaded`.
2017
+ - **`bascik --check` cannot see a component that only a build script prints** and reports it as unused.
2018
+ - **Tag names need a hyphen.** `Card.jsx` becomes `info-card`.
2019
+
2020
+ ### From Next.js
2021
+
2022
+ Full guide: `/switch/from-next`. Key Next.js-specific mappings:
2023
+
2024
+ | Next.js | Bascik |
2025
+ |---------|--------|
2026
+ | `app/page.tsx`, `app/posts/[slug]/page.tsx` | `src/pages/index.html`, `src/pages/posts/[slug].html` (`/posts/<slug>`, also served with a trailing slash) |
2027
+ | `generateStaticParams` | `<script data-bascik-routes>` returning `[{ params: { slug: "..." } }]` |
2028
+ | `generateMetadata` / `metadata` export | A build script in `<head>` that returns `<title>` and `<meta>` from a `src/lib` helper; absolute URLs from `BASCIK_SITE_URL` |
2029
+ | `app/layout.tsx` | Pages keep `<html>`/`<head>`/`<body>`; a head component plus header/footer components |
2030
+ | Server component that maps data to JSX | A `src/lib` helper returning escaped HTML strings, returned by a build script |
2031
+ | `"use client"` component with `useState`/`useEffect` | A component with a plain `<script>`; persistent state lives in the DOM or `localStorage` |
2032
+ | `next/link`, `next/image`, `next/font` | `<a>`, `<img>` with `width`/`height`, a self-hosted `@font-face` plus `<link rel="preload">` |
2033
+ | CSS Modules (`styles.x`) | Paired component `.css`; a class written in the template is scoped |
2034
+ | Tailwind via PostCSS | A `pipeline.exec` `pre` script running PostCSS + Tailwind into `BASCIK_OUT_DIR`; Tailwind `content` lists `src/**/*.html` and `src/lib/**/*.ts` |
2035
+ | `app/api/x/route.ts` | `src/api/x.ts` exporting `GET`/`POST`; `context.params` is a plain object, not a promise; `bascik --server` only |
2036
+
2037
+ Next.js-specific gotchas:
2038
+
2039
+ - **`@/` in a helper that imports a helper fails.** tsconfig `paths` do not apply; Node runs `src/lib/*.ts` directly. Inside helpers use relative paths with the `.ts` extension, and `import type` for type-only imports.
2040
+ - **Tailwind `@apply` in component CSS is not processed.** Write the CSS out, or keep `@apply` in the global Tailwind input file.
2041
+ - **Tailwind's `dark` class works unscoped** when it is toggled on `<html>` and the component's own CSS does not define it.
2042
+ - **A pre-paint script (theme, FOUC) goes in `<head>`.** Production HTML minification moves body scripts to the end of the body; head scripts stay put.
2043
+ - **Style slotted Markdown with `.wrapper :is(h2)`**, not `.wrapper h2` (see Markdown above).
2044
+ - **Unknown slugs return 404.** The upstream example answers 500 for them, because `getPostBySlug` throws before `notFound()`.
1954
2045
 
1955
2046
  ### From Svelte
1956
2047
 
@@ -1977,12 +2068,47 @@ Full guide: `/switch/from-vue`. Key Vue-specific mappings:
1977
2068
  |-----|--------|
1978
2069
  | `<template>` + `<style scoped>` | `.html` + `.css` paired files |
1979
2070
  | `<slot />` | `data-bascik-slot` (no value) |
1980
- | `<slot name="x" />` | `data-bascik-slot="x"` |
1981
- | `defineProps` | `data-bascik-prop-*` attributes |
1982
- | `ref` / `reactive` | Vanilla JS `<script>` |
1983
- | `v-if` / `v-show` | CSS `display:none` or JS toggle |
1984
- | `vue-router` | One `.html` per route in `src/pages/` |
1985
- | `onMounted` data fetch | `<script data-bascik-build>` |
2071
+ | `<slot name="x" />` / `<template #x>` | `<div data-bascik-slot="x">` wrapper around the content, in the component and at the usage site |
2072
+ | `defineProps` | `data-bascik-prop-*` attributes (text only) |
2073
+ | Array or object prop (`:data="rows"`) | JSON in a `<script type="application/json">` that fills the default slot; a build script prints it from a `src/lib` helper, and `<` is escaped as `\u003c` |
2074
+ | `$emit`, `v-model` | DOM events: `dispatchEvent(new CustomEvent(...))` and `addEventListener` |
2075
+ | `ref` / `reactive` | Vanilla JS `<script>`; the script updates the DOM itself |
2076
+ | `v-if` / `v-show` | Build-time: a build script prints one branch. Runtime: toggle the `hidden` attribute |
2077
+ | `v-for` | Build-time loop in a build script, or clone a `<template>` from a script |
2078
+ | `vue-router` | One `.html` per route in `src/pages/`; parameterized routes use `src/pages/blog/[slug].html` with `<script data-bascik-routes>` |
2079
+ | `onMounted` data fetch | `<script data-bascik-build>` for data that is the same for everyone, `fetch()` in a component script otherwise |
2080
+
2081
+ Vue-specific gotchas:
2082
+
2083
+ - **Named-slot wrappers are removed.** `<h1 data-bascik-slot="header">Title</h1>` at the usage site produces `Title` with no `<h1>` (an `<a>` loses its `href` too). Write `<div data-bascik-slot="header"><h1>Title</h1></div>`.
2084
+ - **Component tag names need a hyphen.** `Card.vue` becomes `info-card`; a tag named `card` warns.
2085
+ - **Script-created elements get no element-selector styles.** CSS such as `td { }` applies to template markup. Put the markup in a `<template>` inside the component and clone it, instead of `document.createElement('td')`.
2086
+ - **Script placement differs between modes.** Production HTML minification moves component scripts to the end of the document; the development server and `minify.html: false` leave them in place. Never rely on a later element existing when the script starts: look it up inside an event handler or after `DOMContentLoaded`. Use `pageshow` to resync a form value the browser restored on Back.
2087
+ - **`bascik --check` cannot see a component that only a build script prints** and reports it as unused.
2088
+
2089
+ ### From WordPress
2090
+
2091
+ Full guide: `/switch/from-wordpress`. Key WordPress-specific mappings:
2092
+
2093
+ | WordPress | Bascik |
2094
+ |-----------|--------|
2095
+ | The Loop, `WP_Query` | A `src/lib` helper returning escaped HTML, printed by a build script |
2096
+ | `single.php` at `/%year%/%monthnum%/%day%/%postname%/` | `src/pages/[year]/[month]/[day]/[slug]/index.html` (one bracket per segment; a param cannot contain `/`) |
2097
+ | Pages and child pages | `src/pages/[pageslug]/index.html` and `src/pages/[parent]/[child]/index.html` |
2098
+ | Category, tag, and `/page/2/` archives | `category/[term]/`, `tag/[term]/`, `page/[page]/` templates, each paged archive its own template |
2099
+ | REST API (`/wp-json/wp/v2/`) | One `pre` exec script fetches everything (follow `X-WP-TotalPages`; `per_page` above 100 is a 400), sanitizes it, downloads media into `BASCIK_OUT_DIR`, and writes a JSON snapshot pages read |
2100
+ | Tools > Export (WXR) | `wordpress-export-to-markdown`, then restore page parents, menu order, term names, and image alt text by hand |
2101
+ | `wp-content/uploads/` | Same path under `dist/`, so content URLs keep working |
2102
+ | `/feed/` | A `post` exec script writing `/feed.xml`; redirect `/feed/` on the host |
2103
+
2104
+ WordPress-specific gotchas:
2105
+
2106
+ - **Sanitize every post body before printing it.** `content.rendered` can hold `<script>`, `on*` handlers, and `javascript:` links; administrators may save any markup. Use an allowlist such as `sanitize-html`, which also drops custom elements so content cannot place a component tag. Bascik itself only removes printed directive scripts.
2107
+ - **Exclude pages that read fetched data from the script cache** (`scripts.cache.exclude`). Network responses are not part of the cache key, so a rebuild after an edit in WordPress otherwise reuses old output.
2108
+ - **Keep page helpers light.** Every build script is its own Node process; import `zod` and `sanitize-html` in the sync step, not in helpers every page loads.
2109
+ - **Raise the exec `timeout` for media downloads.** The default is 60 seconds.
2110
+ - **Titles are HTML over REST** (`&#038;`, curly quotes); convert to text, then escape. WXR titles are the raw text.
2111
+ - **No comments, search, or date and author archives** in a static build; use services or add templates.
1986
2112
 
1987
2113
  ### Migrating Existing Sitemap & Robots Files
1988
2114
 
package/dist/cli.d.ts CHANGED
@@ -1,5 +1,6 @@
1
1
  export interface CliOptions {
2
2
  yesFlag: boolean;
3
3
  noDevFlag: boolean;
4
+ noInstallFlag: boolean;
4
5
  }
5
6
  export declare function parseCliOptions(args: string[]): CliOptions;
package/dist/cli.js CHANGED
@@ -2,6 +2,7 @@ export function parseCliOptions(args) {
2
2
  return {
3
3
  yesFlag: args.includes("-y") || args.includes("--yes"),
4
4
  noDevFlag: args.includes("--no-dev"),
5
+ noInstallFlag: args.includes("--no-install"),
5
6
  };
6
7
  }
7
8
  //# sourceMappingURL=cli.js.map
package/dist/cli.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAKA,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtD,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;KACrC,CAAC;AACJ,CAAC"}
1
+ {"version":3,"file":"cli.js","sourceRoot":"","sources":["../src/cli.ts"],"names":[],"mappings":"AAMA,MAAM,UAAU,eAAe,CAAC,IAAc;IAC5C,OAAO;QACL,OAAO,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,IAAI,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC;QACtD,SAAS,EAAE,IAAI,CAAC,QAAQ,CAAC,UAAU,CAAC;QACpC,aAAa,EAAE,IAAI,CAAC,QAAQ,CAAC,cAAc,CAAC;KAC7C,CAAC;AACJ,CAAC"}
package/dist/index.js CHANGED
@@ -5,7 +5,7 @@ import { join } from "node:path";
5
5
  import { parseCliOptions } from "./cli.js";
6
6
  import { scaffold, validateProjectName } from "./scaffold.js";
7
7
  const args = process.argv.slice(2);
8
- const { yesFlag, noDevFlag } = parseCliOptions(args);
8
+ const { yesFlag, noDevFlag, noInstallFlag } = parseCliOptions(args);
9
9
  const nameArg = args.find((a) => !a.startsWith("-"));
10
10
  const rl = createInterface({ input: process.stdin, output: process.stdout });
11
11
  let projectName;
@@ -28,7 +28,7 @@ console.log(`✓ Scaffolded ${projectName}/\n`);
28
28
  let shouldInstall;
29
29
  let shouldDev;
30
30
  if (yesFlag) {
31
- shouldInstall = true;
31
+ shouldInstall = !noInstallFlag;
32
32
  shouldDev = !noDevFlag;
33
33
  }
34
34
  else {
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -16,14 +16,14 @@ export declare const FAVICON_SVG = "<svg viewBox=\"0 0 32 32\" width=\"32\" heig
16
16
  export declare const FAVICON_ICO_B64 = "AAABAAMAEBAAAAEAIADIAAAANgAAACAgAAABACAACAEAAP4AAAAwMAAAAQAgAGcBAAAGAgAiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeklEQVQ4jWNgoDa4/L93/uX/vf8J4577OAzouU+cAb3zMTSf/9+vQKTm/xf/9yRgGHDxf08CsqINV8vBGJsBIMsI+r+g3Q+MyfZ/AU4DiPR/AQ4DiPL/ZTwGEOX/yzgNICH+C7C7gPj434AlGon2/2UcmGj/0y/9UwoAAEX+8tkvEHwAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAAJcEhZcwAACxMAAAsTAQCanBgAAAC6SURBVFiF7ZbRDYMwDEQzwcEIDFC6QJXf0jEYKBEjMBJCGYBRXNEFepZshaqx5O+8+HQ+h9DqS22yDEWy2HY6Alu7pNkeIK80QJG8WgOcn1IApMMa4JSVenxr+svF9X88bxKnsZ7+6PpPV/M/1ADG/ocSwNz/UAKY+x8qAIf9D90E7Pc/FAAu+x8KAJf9DxrAKf/BT8An/0ECuOU/SAC3/I/TKPF1r6N/4bvdf4OL/oXqpn/6kfv/r+sNLlrC+A4T1ksAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAMAAAADAIBgAAAFcC+YcAAAAJcEhZcwAACxMAAAsTAQCanBgAAAEZSURBVGiB7ZjBDcJADARTAVBCKKKGSwfpgo+PlHDfQDOWSQGpBIwuD/7EVpYIr2Qpr+hGXln2Nk0otL0mzWXSrJii2QGAZhxALqbHsw4t8PEqSskEUH+ABGAdWhNA+F/D/+sV/teY/9vNf3mS0piWerxof/OfxqSH42mpfOv2N//7y/kDUL935//eFSBvv/94Aghi//EEYMT+4wdAmP3fsQMFsv97AQhq//cCYNT+7wNAuPvXqQMFdv96AAjy/vUAYOT9awcgAANi40iAAAn3/00EAB2U7f56m/2eQf30E14yR/0AAAABJRU5ErkJggg==";
17
17
  export declare const FAVICON_32_PNG_B64 = "iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuklEQVRYhe2W0Q2DMAxEM8HBCAxQukCV39IxGCgRIzASQhmAUVzRBXqWbIWqseTvvPh0PofQ6kttsgxFsth2OgJbu6TZHiCvNECRvFoDnJ9SAKTDGuCUlXp8a/rLxfV/PG8Sp7Ge/uj6T1fzP9QAxv6HEsDc/1ACmPsfKgCH/Q/dBOz3PxQALvsfCgCX/Q8awCn/wU/AJ/9BArjlP0gAt/yP0yjxda+jf+G73X+Di/6F6qZ/+pH7/6/rDS5awvgOE9ZLAAAAAElFTkSuQmCC";
18
18
  export declare const APPLE_TOUCH_ICON_PNG_B64 = "iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGKUlEQVR4nO2bzWqdVRSGN72AtqO0WicWJ1KtoTi0dGjTK8gFGGtnH1ghJpXOw1l0kHoTUvlIU6rQa9Dg2R9W8OcK2guwky0nkIGU0kTfsvZZ63nhnWWwz7ufnTwcSCmEEEIIIYQQQgghhBBCCCGEEEII6SG12Xptsyd0Fm2DrZIxtdlYmzVq0TYYSra0dvfUvM2edTA+bdoNpnZvtWTL4kMDU7zHNG+z54tfViVbFn+WvMen9iY2GEvG4M9hH9RQsgV/jtsJf/a/BGqSDfBnYGrBNsCfO7gE2mQb4M8AZWE2wJ87uARqkg3wZ2CK9pjGkjF8/xy2Q8kWvn+O24nvn/0vgZpkA/wZmKI9prFkDP4ctkPJFvw5bif82f8SqEk2wJ+BKdpjGkvG4M9hO5RswZ/jdsKf/S+BmmQD/BmYoj2msWQM/hy2Q8kW/DluJ/zZ/xKoSTbAn4Ep2mMaS8bgz2E7lGzBn+N2wp/9L4GaZAP8GZiiPaaxZAz+HLZDyRb8OW4n/Nn/EqhJNsCfgSnaYxpLxuDPYTuUbMGf43bCn5evj3/farsPNw77+I9t9/PUToo/L1n3ft1sn3z6YTt95uy/evX65bb3dNP9fNW/+POy9Lufvmzn3l55Ceajnr+w0h4c3HY/Z/Ut/rwM/fnvnfb+6ruvhPmol65cbAcvdtzPW52KPy9Jd/c+ey3MR72/v+F+3upQ/HmJenN77dhAf3Fnzf281af487J0/da1YwO9+Fnv81af4s8dXAJAN81G+HMHoAK0SfbBnzuAFKBNuRH+7A0pQJtyI/zZG1KANtk++HMHkAK0SbbBnzsAFKBNuQ/+7A0oQJtyH/zZG1CANtk2+HMHgAK0SXbBnzuAE6BNuQ3+7A0nQJtyG/zZG04ANtku+HMHcAK0STbBnzsAE6BNuQv+7A0mQJtyF/zZG0yANtkm+HMHYAK0SfbAnzuAEqBNuQn+7A0lQJtyE/zZG0qANtke+HMHUAK0SbbAnzsAEgBNuQf+7A0kQJtyD/zZG0iANtkW+HMHQAK0SXbAnzuAEaBNuQX+7A0jQJtyC/zZG0aANtkO+HMHMAK0STbAnzsAEQBNuQP+7A0iQJtyB/zZG0SANtkG+HMHIAK0ZgP8uQMIAdqUG+DP3hACtCk3wJ+9IQRok22AP3cAIUCbZAP8uQMAAdqUG+DP3gACtCk3wJ+9AQRok22AP3cAIECbZAP8uQP4ANqUG+DP3vABtCk3wJ+94QNok22AP3cAH0CbZAP8uQPwANqUG+DP3uABtCk3wJ+9wQNok22AP3cAHkCbZAP8uQPoANqUG+DP3tABtCk3wJ+9oQNok22AP3cAHUCbZAP8uQPgANqUG+DP3sABtCk3wJ+9gQNok22AP3cAHECbZAP8uQPYANqUG+DP3rABtCk3wJ+9YQNok22AP3cAG0CbZAP8uQPQANqUG+DP3qABtCk3wJ+9QQNok22AP3cAGkCbZAP8uQPIANqUG+DPmXpze62dPnP2WL31zQ3389aTF3/O1N2HG8cG+ttHn7uft56w+HOyHrzYaZeuXHwtzB98/N7hz3qft56g+HPSPji43c5fWHklzG+9c659/8tX7uesJy/+nLV7Tzfb1euXX4L52o2P2v5vX7ufr/634s/Z+8Of2+3+/sZhf/zrjvt56v8o/tzBJVCTbIA/A1O0xzSWjFl88A7Gp02+Af4MWHEe1tTurZZsWXxo7+GpyTfAnwEr2sMaS8bgz2E7lGxp7e6peZs962B82rQb4M9AFeZRzdvs+eKXVcmWxZ8l7/GpvYnW/d2iwC0CBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIEC";
19
- /** Head component — charset, viewport, favicon, CSS link. */
19
+ /** Head component: charset, viewport, favicon, CSS link. */
20
20
  export declare const SITE_META_HTML = "<meta charset=\"UTF-8\" />\n<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\" />\n<meta name=\"description\" content=\"A fast, modern website built with Bascik.\" />\n<link rel=\"icon\" href=\"/favicon.ico\" sizes=\"any\" />\n<link rel=\"icon\" type=\"image/png\" sizes=\"32x32\" href=\"/assets/favicon-32x32.png\" />\n<link rel=\"icon\" type=\"image/svg+xml\" href=\"/assets/favicon.svg\" />\n<link rel=\"apple-touch-icon\" href=\"/assets/apple-touch-icon.png\" />\n<link rel=\"stylesheet\" href=\"/css/styles.css\" />\n";
21
21
  export declare const SITE_HEADER_CSS = ".header {\n position: sticky;\n top: 0;\n z-index: 100;\n background: rgba(24, 25, 27, 0.9);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border);\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.nav {\n display: flex;\n align-items: center;\n height: 60px;\n gap: 24px;\n}\n\n.logo {\n font-weight: 700;\n font-size: 1rem;\n color: var(--text);\n letter-spacing: -0.01em;\n flex-shrink: 0;\n}\n.logo:hover { opacity: 0.8; }\n\n.nav-menu {\n list-style: none;\n margin: 0 0 0 auto;\n padding: 0;\n display: flex;\n gap: 4px;\n}\n.nav-menu a {\n display: block;\n padding: 6px 12px;\n font-size: 0.9rem;\n color: var(--text-muted);\n border-radius: var(--r-sm);\n transition: color .15s, background .15s;\n}\n.nav-menu a:hover { color: var(--text); background: var(--elevated); opacity: 1; }\n\n.nav-toggle {\n display: none;\n flex-direction: column;\n justify-content: center;\n gap: 5px;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n margin-left: auto;\n border-radius: var(--r-sm);\n}\n.nav-toggle span {\n display: block;\n height: 2px;\n background: var(--text);\n border-radius: 2px;\n}\n\n@media (max-width: 640px) {\n .nav-toggle { display: flex; }\n\n .nav-menu {\n display: none;\n position: absolute;\n top: 60px;\n left: 0;\n right: 0;\n flex-direction: column;\n gap: 0;\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 8px 16px;\n }\n .nav-menu.is-open { display: flex; }\n .nav-menu li { width: 100%; }\n .nav-menu a { padding: 10px 4px; }\n}\n";
22
22
  /** Sticky top nav with logo (brand prop) and mobile hamburger. */
23
23
  export declare const SITE_HEADER_HTML = "<style>\n.header {\n position: sticky;\n top: 0;\n z-index: 100;\n background: rgba(24, 25, 27, 0.9);\n backdrop-filter: blur(12px);\n -webkit-backdrop-filter: blur(12px);\n border-bottom: 1px solid var(--border);\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.nav {\n display: flex;\n align-items: center;\n height: 60px;\n gap: 24px;\n}\n\n.logo {\n font-weight: 700;\n font-size: 1rem;\n color: var(--text);\n letter-spacing: -0.01em;\n flex-shrink: 0;\n}\n.logo:hover { opacity: 0.8; }\n\n.nav-menu {\n list-style: none;\n margin: 0 0 0 auto;\n padding: 0;\n display: flex;\n gap: 4px;\n}\n.nav-menu a {\n display: block;\n padding: 6px 12px;\n font-size: 0.9rem;\n color: var(--text-muted);\n border-radius: var(--r-sm);\n transition: color .15s, background .15s;\n}\n.nav-menu a:hover { color: var(--text); background: var(--elevated); opacity: 1; }\n\n.nav-toggle {\n display: none;\n flex-direction: column;\n justify-content: center;\n gap: 5px;\n width: 36px;\n height: 36px;\n background: none;\n border: none;\n cursor: pointer;\n padding: 6px;\n margin-left: auto;\n border-radius: var(--r-sm);\n}\n.nav-toggle span {\n display: block;\n height: 2px;\n background: var(--text);\n border-radius: 2px;\n}\n\n@media (max-width: 640px) {\n .nav-toggle { display: flex; }\n\n .nav-menu {\n display: none;\n position: absolute;\n top: 60px;\n left: 0;\n right: 0;\n flex-direction: column;\n gap: 0;\n background: var(--surface);\n border-bottom: 1px solid var(--border);\n padding: 8px 16px;\n }\n .nav-menu.is-open { display: flex; }\n .nav-menu li { width: 100%; }\n .nav-menu a { padding: 10px 4px; }\n}\n</style>\n<header class=\"header\">\n <div class=\"container\">\n <nav class=\"nav\">\n <a href=\"/\" class=\"logo\" data-bascik-prop-brand>My Site</a>\n <button id=\"nav-toggle\" class=\"nav-toggle\" aria-label=\"Toggle menu\" aria-expanded=\"false\" data-testid=\"nav-toggle\">\n <span></span><span></span><span></span>\n </button>\n <ul id=\"nav-menu\" class=\"nav-menu\" data-testid=\"nav-menu\">\n <li><a href=\"/\">Home</a></li>\n <li><a href=\"/about\">About</a></li>\n <li><a href=\"/contact\">Contact</a></li>\n </ul>\n </nav>\n </div>\n</header>\n<script>\n var toggle = document.getElementById('nav-toggle');\n var menu = document.getElementById('nav-menu');\n toggle.addEventListener('click', function () {\n var open = toggle.getAttribute('aria-expanded') === 'true';\n toggle.setAttribute('aria-expanded', String(!open));\n menu.classList.toggle('is-open');\n });\n</script>\n";
24
24
  export declare const SITE_FOOTER_CSS = ".footer {\n border-top: 1px solid var(--border);\n padding: 28px 0;\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.footer-inner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.footer-copy { font-size: 0.85rem; color: var(--text-muted); }\n\n.footer-nav {\n display: flex;\n gap: 20px;\n}\n.footer-nav a {\n font-size: 0.85rem;\n color: var(--text-muted);\n}\n.footer-nav a:hover { color: var(--text); opacity: 1; }\n\n@media (max-width: 480px) {\n .footer-inner { flex-direction: column; align-items: flex-start; }\n}\n";
25
25
  /** Footer with brand prop and build-time year. */
26
- export declare const SITE_FOOTER_HTML = "<style>\n.footer {\n border-top: 1px solid var(--border);\n padding: 28px 0;\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.footer-inner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.footer-copy { font-size: 0.85rem; color: var(--text-muted); }\n\n.footer-nav {\n display: flex;\n gap: 20px;\n}\n.footer-nav a {\n font-size: 0.85rem;\n color: var(--text-muted);\n}\n.footer-nav a:hover { color: var(--text); opacity: 1; }\n\n@media (max-width: 480px) {\n .footer-inner { flex-direction: column; align-items: flex-start; }\n}\n</style>\n<footer class=\"footer\">\n <div class=\"container\">\n <div class=\"footer-inner\">\n <span class=\"footer-copy\">\n &copy; <script data-bascik-build>console.log(new Date().getFullYear())</script>\n <span data-bascik-prop-brand>My Site</span>\n </span>\n <nav class=\"footer-nav\" aria-label=\"Footer navigation\">\n <a href=\"/\">Home</a>\n <a href=\"/about\">About</a>\n <a href=\"/contact\">Contact</a>\n </nav>\n </div>\n </div>\n</footer>\n";
26
+ export declare const SITE_FOOTER_HTML = "<style>\n.footer {\n border-top: 1px solid var(--border);\n padding: 28px 0;\n}\n\n.container { max-width: 1140px; margin: 0 auto; padding: 0 28px; }\n\n.footer-inner {\n display: flex;\n align-items: center;\n justify-content: space-between;\n gap: 16px;\n flex-wrap: wrap;\n}\n\n.footer-copy { font-size: 0.85rem; color: var(--text-muted); }\n\n.footer-nav {\n display: flex;\n gap: 20px;\n}\n.footer-nav a {\n font-size: 0.85rem;\n color: var(--text-muted);\n}\n.footer-nav a:hover { color: var(--text); opacity: 1; }\n\n@media (max-width: 480px) {\n .footer-inner { flex-direction: column; align-items: flex-start; }\n}\n</style>\n<footer class=\"footer\">\n <div class=\"container\">\n <div class=\"footer-inner\">\n <span class=\"footer-copy\">\n &copy; <script data-bascik-build>export default () => String(new Date().getFullYear());</script>\n <span data-bascik-prop-brand>My Site</span>\n </span>\n <nav class=\"footer-nav\" aria-label=\"Footer navigation\">\n <a href=\"/\">Home</a>\n <a href=\"/about\">About</a>\n <a href=\"/contact\">Contact</a>\n </nav>\n </div>\n </div>\n</footer>\n";
27
27
  export declare const FEAT_CARD_CSS = ".fcard {\n background: var(--elevated);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 24px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n transition: border-color .2s, box-shadow .2s, transform .2s;\n}\n.fcard:hover {\n border-color: var(--border-hover);\n box-shadow: 0 0 0 1px var(--accent-dim), 0 8px 32px rgba(0,0,0,0.3);\n transform: translateY(-2px);\n}\n\n.fcard-header {\n font-size: 0.7rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: .1em;\n color: var(--accent);\n}\n.fcard-header:empty { display: none; }\n\n.fcard-body { flex: 1; }\n.fcard-body h3 { margin: 0 0 6px; font-size: 1rem; color: var(--text); }\n.fcard-body p { margin: 0; font-size: 0.88rem; color: var(--text-muted); }\n\n.fcard-footer {\n padding-top: 12px;\n border-top: 1px solid var(--border);\n margin-top: auto;\n}\n.fcard-footer:empty { display: none; }\n";
28
28
  /**
29
29
  * Card component demonstrating named slots.
@@ -31,7 +31,7 @@ export declare const FEAT_CARD_CSS = ".fcard {\n background: var(--elevated);\n
31
31
  */
32
32
  export declare const FEAT_CARD_HTML = "<style>\n.fcard {\n background: var(--elevated);\n border: 1px solid var(--border);\n border-radius: var(--r);\n padding: 24px;\n display: flex;\n flex-direction: column;\n gap: 12px;\n transition: border-color .2s, box-shadow .2s, transform .2s;\n}\n.fcard:hover {\n border-color: var(--border-hover);\n box-shadow: 0 0 0 1px var(--accent-dim), 0 8px 32px rgba(0,0,0,0.3);\n transform: translateY(-2px);\n}\n\n.fcard-header {\n font-size: 0.7rem;\n font-weight: 700;\n text-transform: uppercase;\n letter-spacing: .1em;\n color: var(--accent);\n}\n.fcard-header:empty { display: none; }\n\n.fcard-body { flex: 1; }\n.fcard-body h3 { margin: 0 0 6px; font-size: 1rem; color: var(--text); }\n.fcard-body p { margin: 0; font-size: 0.88rem; color: var(--text-muted); }\n\n.fcard-footer {\n padding-top: 12px;\n border-top: 1px solid var(--border);\n margin-top: auto;\n}\n.fcard-footer:empty { display: none; }\n</style>\n<article class=\"fcard\">\n <div class=\"fcard-header\" data-bascik-slot=\"header\"></div>\n <div class=\"fcard-body\" data-bascik-slot></div>\n <div class=\"fcard-footer\" data-bascik-slot=\"footer\"></div>\n</article>\n";
33
33
  export declare const MY_COUNTER_CSS = ".counter {\n display: inline-flex;\n align-items: center;\n gap: 16px;\n font-size: 1.25rem;\n}\n\n.count-val {\n font-family: var(--mono);\n font-size: 1.5rem;\n min-width: 2ch;\n text-align: center;\n color: var(--accent);\n}\n\n.btn {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 10px 22px; border: none; border-radius: var(--r-sm);\n font-size: 0.9rem; font-weight: 600; cursor: pointer;\n text-decoration: none; transition: filter .15s, opacity .15s; line-height: 1;\n}\n.btn-primary { background: var(--accent); color: #18191b; }\n.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }\n@media (hover: hover) {\n .btn-primary:hover { filter: brightness(0.88); opacity: 1; }\n .btn-ghost:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }\n}\n";
34
- /** Interactive counter — two instances on a page stay fully independent. */
34
+ /** Interactive counter: two instances on a page stay fully independent. */
35
35
  export declare const MY_COUNTER_HTML = "<style>\n.counter {\n display: inline-flex;\n align-items: center;\n gap: 16px;\n font-size: 1.25rem;\n}\n\n.count-val {\n font-family: var(--mono);\n font-size: 1.5rem;\n min-width: 2ch;\n text-align: center;\n color: var(--accent);\n}\n\n.btn {\n display: inline-flex; align-items: center; gap: 8px;\n padding: 10px 22px; border: none; border-radius: var(--r-sm);\n font-size: 0.9rem; font-weight: 600; cursor: pointer;\n text-decoration: none; transition: filter .15s, opacity .15s; line-height: 1;\n}\n.btn-primary { background: var(--accent); color: #18191b; }\n.btn-ghost { background: transparent; color: var(--text); border: 1px solid var(--border); }\n@media (hover: hover) {\n .btn-primary:hover { filter: brightness(0.88); opacity: 1; }\n .btn-ghost:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }\n}\n</style>\n<div class=\"counter\">\n <button id=\"btn-dec\" class=\"btn btn-ghost\" data-testid=\"counter-dec\">\u2212</button>\n <span id=\"count-val\" class=\"count-val\" data-testid=\"counter-val\">0</span>\n <button id=\"btn-inc\" class=\"btn btn-primary\" data-testid=\"counter-inc\">+</button>\n</div>\n<script>\n (function () {\n var count = 0;\n var val = document.getElementById('count-val');\n document.getElementById('btn-dec').addEventListener('click', function () {\n val.textContent = --count;\n });\n document.getElementById('btn-inc').addEventListener('click', function () {\n val.textContent = ++count;\n });\n }());\n</script>\n";
36
36
  export declare const SITE_META_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-meta component', () => {\n const path = join(process.cwd(), 'src/components/site-meta/site-meta.html');\n\n it('contains charset, viewport, favicon, and stylesheet link tags', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('charset=\"UTF-8\"');\n expect(html).toContain('name=\"viewport\"');\n expect(html).toContain('name=\"description\"');\n expect(html).toContain('favicon.ico');\n expect(html).toContain('favicon-32x32.png');\n expect(html).toContain('favicon.svg');\n expect(html).toContain('apple-touch-icon.png');\n expect(html).toContain('styles.css');\n });\n});\n";
37
37
  export declare const SITE_HEADER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-header component', () => {\n const path = join(process.cwd(), 'src/components/site-header/site-header.html');\n\n it('renders brand prop placeholder and navigation links', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('data-bascik-prop-brand');\n expect(html).toContain('href=\"/\"');\n expect(html).toContain('href=\"/about\"');\n expect(html).toContain('href=\"/contact\"');\n expect(html).toContain('id=\"nav-toggle\"');\n });\n});\n";
package/dist/scaffold.js CHANGED
@@ -26,10 +26,10 @@ export const PACKAGE_JSON = (name) => JSON.stringify({
26
26
  e2e: "playwright test --config e2e/playwright.config.ts",
27
27
  },
28
28
  dependencies: {
29
- "@bascik/bascik": "^1.0.0-rc.2",
29
+ "@bascik/bascik": "^1.0.0-rc.4",
30
30
  },
31
31
  devDependencies: {
32
- "@bascik/language-server": "^0.1.0-rc.1",
32
+ "@bascik/language-server": "^0.1.0-rc.2",
33
33
  "@playwright/test": "^1.62.0",
34
34
  "@types/node": "^24.0.0",
35
35
  "@vitest/coverage-v8": "^4.1.10",
@@ -294,7 +294,7 @@ export const FAVICON_SVG = `<svg viewBox="0 0 32 32" width="32" height="32" xmln
294
294
  export const FAVICON_ICO_B64 = `AAABAAMAEBAAAAEAIADIAAAANgAAACAgAAABACAACAEAAP4AAAAwMAAAAQAgAGcBAAAGAgAiVBORw0KGgoAAAANSUhEUgAAABAAAAAQCAYAAAAf8/9hAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAeklEQVQ4jWNgoDa4/L93/uX/vf8J4577OAzouU+cAb3zMTSf/9+vQKTm/xf/9yRgGHDxf08CsqINV8vBGJsBIMsI+r+g3Q+MyfZ/AU4DiPR/AQ4DiPL/ZTwGEOX/yzgNICH+C7C7gPj434AlGon2/2UcmGj/0y/9UwoAAEX+8tkvEHwAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAIAAAACAIBgAAAHN6evQAAAAJcEhZcwAACxMAAAsTAQCanBgAAAC6SURBVFiF7ZbRDYMwDEQzwcEIDFC6QJXf0jEYKBEjMBJCGYBRXNEFepZshaqx5O+8+HQ+h9DqS22yDEWy2HY6Alu7pNkeIK80QJG8WgOcn1IApMMa4JSVenxr+svF9X88bxKnsZ7+6PpPV/M/1ADG/ocSwNz/UAKY+x8qAIf9D90E7Pc/FAAu+x8KAJf9DxrAKf/BT8An/0ECuOU/SAC3/I/TKPF1r6N/4bvdf4OL/oXqpn/6kfv/r+sNLlrC+A4T1ksAAAAASUVORK5CYIKJUE5HDQoaCgAAAA1JSERSAAAAMAAAADAIBgAAAFcC+YcAAAAJcEhZcwAACxMAAAsTAQCanBgAAAEZSURBVGiB7ZjBDcJADARTAVBCKKKGSwfpgo+PlHDfQDOWSQGpBIwuD/7EVpYIr2Qpr+hGXln2Nk0otL0mzWXSrJii2QGAZhxALqbHsw4t8PEqSskEUH+ABGAdWhNA+F/D/+sV/teY/9vNf3mS0piWerxof/OfxqSH42mpfOv2N//7y/kDUL935//eFSBvv/94Aghi//EEYMT+4wdAmP3fsQMFsv97AQhq//cCYNT+7wNAuPvXqQMFdv96AAjy/vUAYOT9awcgAANi40iAAAn3/00EAB2U7f56m/2eQf30E14yR/0AAAABJRU5ErkJggg==`;
295
295
  export const FAVICON_32_PNG_B64 = `iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAACXBIWXMAAAsTAAALEwEAmpwYAAAAuklEQVRYhe2W0Q2DMAxEM8HBCAxQukCV39IxGCgRIzASQhmAUVzRBXqWbIWqseTvvPh0PofQ6kttsgxFsth2OgJbu6TZHiCvNECRvFoDnJ9SAKTDGuCUlXp8a/rLxfV/PG8Sp7Ge/uj6T1fzP9QAxv6HEsDc/1ACmPsfKgCH/Q/dBOz3PxQALvsfCgCX/Q8awCn/wU/AJ/9BArjlP0gAt/yP0yjxda+jf+G73X+Di/6F6qZ/+pH7/6/rDS5awvgOE9ZLAAAAAElFTkSuQmCC`;
296
296
  export const APPLE_TOUCH_ICON_PNG_B64 = `iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGKUlEQVR4nO2bzWqdVRSGN72AtqO0WicWJ1KtoTi0dGjTK8gFGGtnH1ghJpXOw1l0kHoTUvlIU6rQa9Dg2R9W8OcK2guwky0nkIGU0kTfsvZZ63nhnWWwz7ufnTwcSCmEEEIIIYQQQgghhBBCCCGEEEII6SG12Xptsyd0Fm2DrZIxtdlYmzVq0TYYSra0dvfUvM2edTA+bdoNpnZvtWTL4kMDU7zHNG+z54tfViVbFn+WvMen9iY2GEvG4M9hH9RQsgV/jtsJf/a/BGqSDfBnYGrBNsCfO7gE2mQb4M8AZWE2wJ87uARqkg3wZ2CK9pjGkjF8/xy2Q8kWvn+O24nvn/0vgZpkA/wZmKI9prFkDP4ctkPJFvw5bif82f8SqEk2wJ+BKdpjGkvG4M9hO5RswZ/jdsKf/S+BmmQD/BmYoj2msWQM/hy2Q8kW/DluJ/zZ/xKoSTbAn4Ep2mMaS8bgz2E7lGzBn+N2wp/9L4GaZAP8GZiiPaaxZAz+HLZDyRb8OW4n/Nn/EqhJNsCfgSnaYxpLxuDPYTuUbMGf43bCn5evj3/farsPNw77+I9t9/PUToo/L1n3ft1sn3z6YTt95uy/evX65bb3dNP9fNW/+POy9Lufvmzn3l55Ceajnr+w0h4c3HY/Z/Ut/rwM/fnvnfb+6ruvhPmol65cbAcvdtzPW52KPy9Jd/c+ey3MR72/v+F+3upQ/HmJenN77dhAf3Fnzf281af487J0/da1YwO9+Fnv81af4s8dXAJAN81G+HMHoAK0SfbBnzuAFKBNuRH+7A0pQJtyI/zZG1KANtk++HMHkAK0SbbBnzsAFKBNuQ/+7A0oQJtyH/zZG1CANtk2+HMHgAK0SXbBnzuAE6BNuQ3+7A0nQJtyG/zZG04ANtku+HMHcAK0STbBnzsAE6BNuQv+7A0mQJtyF/zZG0yANtkm+HMHYAK0SfbAnzuAEqBNuQn+7A0lQJtyE/zZG0qANtke+HMHUAK0SbbAnzsAEgBNuQf+7A0kQJtyD/zZG0iANtkW+HMHQAK0SXbAnzuAEaBNuQX+7A0jQJtyC/zZG0aANtkO+HMHMAK0STbAnzsAEQBNuQP+7A0iQJtyB/zZG0SANtkG+HMHIAK0ZgP8uQMIAdqUG+DP3hACtCk3wJ+9IQRok22AP3cAIUCbZAP8uQMAAdqUG+DP3gACtCk3wJ+9AQRok22AP3cAIECbZAP8uQP4ANqUG+DP3vABtCk3wJ+94QNok22AP3cAH0CbZAP8uQPwANqUG+DP3uABtCk3wJ+9wQNok22AP3cAHkCbZAP8uQPoANqUG+DP3tABtCk3wJ+9oQNok22AP3cAHUCbZAP8uQPgANqUG+DP3sABtCk3wJ+9gQNok22AP3cAHECbZAP8uQPYANqUG+DP3rABtCk3wJ+9YQNok22AP3cAG0CbZAP8uQPQANqUG+DP3qABtCk3wJ+9QQNok22AP3cAGkCbZAP8uQPIANqUG+DPmXpze62dPnP2WL31zQ3389aTF3/O1N2HG8cG+ttHn7uft56w+HOyHrzYaZeuXHwtzB98/N7hz3qft56g+HPSPji43c5fWHklzG+9c659/8tX7uesJy/+nLV7Tzfb1euXX4L52o2P2v5vX7ufr/634s/Z+8Of2+3+/sZhf/zrjvt56v8o/tzBJVCTbIA/A1O0xzSWjFl88A7Gp02+Af4MWHEe1tTurZZsWXxo7+GpyTfAnwEr2sMaS8bgz2E7lGxp7e6peZs962B82rQb4M9AFeZRzdvs+eKXVcmWxZ8l7/GpvYnW/d2iwC0CBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIEC`;
297
- /** Head component — charset, viewport, favicon, CSS link. */
297
+ /** Head component: charset, viewport, favicon, CSS link. */
298
298
  export const SITE_META_HTML = `<meta charset="UTF-8" />
299
299
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
300
300
  <meta name="description" content="A fast, modern website built with Bascik." />
@@ -456,7 +456,7 @@ ${SITE_FOOTER_CSS}</style>
456
456
  <div class="container">
457
457
  <div class="footer-inner">
458
458
  <span class="footer-copy">
459
- &copy; <script data-bascik-build>console.log(new Date().getFullYear())</script>
459
+ &copy; <script data-bascik-build>export default () => String(new Date().getFullYear());</script>
460
460
  <span data-bascik-prop-brand>My Site</span>
461
461
  </span>
462
462
  <nav class="footer-nav" aria-label="Footer navigation">
@@ -544,7 +544,7 @@ export const MY_COUNTER_CSS = `.counter {
544
544
  .btn-ghost:hover { border-color: var(--accent); color: var(--accent); opacity: 1; }
545
545
  }
546
546
  `;
547
- /** Interactive counter — two instances on a page stay fully independent. */
547
+ /** Interactive counter: two instances on a page stay fully independent. */
548
548
  export const MY_COUNTER_HTML = `<style>
549
549
  ${MY_COUNTER_CSS}</style>
550
550
  <div class="counter">
@@ -665,7 +665,7 @@ ${body}
665
665
  </body>
666
666
  </html>
667
667
  `;
668
- export const indexPage = (brand) => pageShell(`Home — ${brand}`, brand, ` <section class="hero">
668
+ export const indexPage = (brand) => pageShell(`Home - ${brand}`, brand, ` <section class="hero">
669
669
  <div class="container">
670
670
  <p class="section-label">Welcome</p>
671
671
  <h1>Build fast with <span>${brand}</span></h1>
@@ -709,14 +709,14 @@ export const indexPage = (brand) => pageShell(`Home — ${brand}`, brand, ` <
709
709
  <div class="container">
710
710
  <p class="section-label">Interactive</p>
711
711
  <h2>Counter component</h2>
712
- <p>Each instance holds its own state. Place the same component twice — they stay independent.</p>
712
+ <p>Each instance holds its own state. Place the same component twice, and they stay independent.</p>
713
713
  <div style="display:flex;gap:48px;flex-wrap:wrap;margin-top:24px;">
714
714
  <my-counter />
715
715
  <my-counter />
716
716
  </div>
717
717
  </div>
718
718
  </section>`);
719
- export const aboutPage = (brand) => pageShell(`About — ${brand}`, brand, ` <div class="container">
719
+ export const aboutPage = (brand) => pageShell(`About - ${brand}`, brand, ` <div class="container">
720
720
  <section class="section">
721
721
  <p class="section-label">About</p>
722
722
  <h1>About us</h1>
@@ -738,7 +738,7 @@ export const aboutPage = (brand) => pageShell(`About — ${brand}`, brand, `
738
738
  </div>
739
739
  </section>
740
740
  </div>`);
741
- export const contactPage = (brand) => pageShell(`Contact — ${brand}`, brand, ` <div class="container">
741
+ export const contactPage = (brand) => pageShell(`Contact - ${brand}`, brand, ` <div class="container">
742
742
  <section class="section">
743
743
  <p class="section-label">Contact</p>
744
744
  <h1>Get in touch</h1>
@@ -761,7 +761,7 @@ export const contactPage = (brand) => pageShell(`Contact — ${brand}`, brand, `
761
761
  </form>
762
762
  </section>
763
763
  </div>`);
764
- export const notFoundPage = (brand) => pageShell(`404 — ${brand}`, brand, ` <div class="container">
764
+ export const notFoundPage = (brand) => pageShell(`404 - ${brand}`, brand, ` <div class="container">
765
765
  <section class="section" style="text-align:center;">
766
766
  <p class="section-label">Error</p>
767
767
  <h1 style="font-size:clamp(4rem,12vw,8rem);color:var(--accent);margin-bottom:8px;">404</h1>
@@ -1 +1 @@
1
- {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"scaffold.js","sourceRoot":"","sources":["../src/scaffold.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "create-bascik",
3
3
  "description": "Create a new Bascik project",
4
- "version": "1.0.0-rc.2",
4
+ "version": "1.0.0-rc.4",
5
5
  "repository": {
6
6
  "type": "git",
7
7
  "url": "git+https://github.com/bascikdev/bascik.git",
package/src/cli.ts CHANGED
@@ -1,11 +1,13 @@
1
1
  export interface CliOptions {
2
2
  yesFlag: boolean;
3
3
  noDevFlag: boolean;
4
+ noInstallFlag: boolean;
4
5
  }
5
6
 
6
7
  export function parseCliOptions(args: string[]): CliOptions {
7
8
  return {
8
9
  yesFlag: args.includes("-y") || args.includes("--yes"),
9
10
  noDevFlag: args.includes("--no-dev"),
11
+ noInstallFlag: args.includes("--no-install"),
10
12
  };
11
- }
13
+ }
package/src/index.ts CHANGED
@@ -6,7 +6,7 @@ import { parseCliOptions } from "./cli.js";
6
6
  import { scaffold, validateProjectName } from "./scaffold.js";
7
7
 
8
8
  const args = process.argv.slice(2);
9
- const { yesFlag, noDevFlag } = parseCliOptions(args);
9
+ const { yesFlag, noDevFlag, noInstallFlag } = parseCliOptions(args);
10
10
  const nameArg = args.find((a) => !a.startsWith("-"));
11
11
 
12
12
  const rl = createInterface({ input: process.stdin, output: process.stdout });
@@ -35,7 +35,7 @@ let shouldInstall: boolean;
35
35
  let shouldDev: boolean;
36
36
 
37
37
  if (yesFlag) {
38
- shouldInstall = true;
38
+ shouldInstall = !noInstallFlag;
39
39
  shouldDev = !noDevFlag;
40
40
  } else {
41
41
  const installAnswer = await rl.question("Install dependencies now? (Y/n) ");
@@ -145,6 +145,9 @@ describe("scaffold-to-compiler integration", () => {
145
145
  const currentYear = String(new Date().getFullYear());
146
146
  expect(indexHtml).toContain(`&copy; ${currentYear}`);
147
147
  expect(aboutHtml).toContain(`&copy; ${currentYear}`);
148
+ // Confirm default export handler was scaffolded in site-footer
149
+ const footerHtml = await readFile(join(projectDir, "src/components/site-footer/site-footer.html"), "utf8");
150
+ expect(footerHtml).toContain("export default () => String(new Date().getFullYear())");
148
151
 
149
152
  // 6. Verify representative assets copied to dist
150
153
  await expect(access(join(distDir, "favicon.ico"))).resolves.toBeUndefined();
@@ -207,4 +210,28 @@ describe("scaffold-to-compiler integration", () => {
207
210
  const combinedOutput = `${buildResult.stdout}\n${buildResult.stderr}`;
208
211
  expect(combinedOutput.toLowerCase()).toMatch(/error|failed|syntaxerror/);
209
212
  }, 45_000);
213
+
214
+ it("negative control: rejects compilation when a build script relies on legacy stdout console.log", async () => {
215
+ const parentDir = await createTempDir("stdout-control");
216
+ const projectName = "legacy-stdout-site";
217
+ const projectDir = join(parentDir, projectName);
218
+
219
+ await scaffold(projectName, parentDir);
220
+
221
+ const legacyStdoutPage = `<!DOCTYPE html>
222
+ <html>
223
+ <head><title>Legacy Stdout</title></head>
224
+ <body>
225
+ <script data-bascik-build>
226
+ console.log("<p>legacy</p>");
227
+ </script>
228
+ </body>
229
+ </html>`;
230
+ await writeFile(join(projectDir, "src/pages/index.html"), legacyStdoutPage, "utf8");
231
+
232
+ const buildResult = await runBascikBuild(projectDir);
233
+ expect(buildResult.exitCode).not.toBe(0);
234
+ const combinedOutput = `${buildResult.stdout}\n${buildResult.stderr}`;
235
+ expect(combinedOutput).toContain("missing default export");
236
+ }, 45_000);
210
237
  });
@@ -60,6 +60,7 @@ describe("parseCliOptions", () => {
60
60
  expect(parseCliOptions(["my-site", "-y"])).toEqual({
61
61
  yesFlag: true,
62
62
  noDevFlag: false,
63
+ noInstallFlag: false,
63
64
  });
64
65
  });
65
66
 
@@ -67,6 +68,17 @@ describe("parseCliOptions", () => {
67
68
  expect(parseCliOptions(["my-site", "--yes", "--no-dev"])).toEqual({
68
69
  yesFlag: true,
69
70
  noDevFlag: true,
71
+ noInstallFlag: false,
72
+ });
73
+ });
74
+
75
+ it("supports skipping install with --no-install", () => {
76
+ expect(
77
+ parseCliOptions(["my-site", "-y", "--no-install", "--no-dev"]),
78
+ ).toEqual({
79
+ yesFlag: true,
80
+ noDevFlag: true,
81
+ noInstallFlag: true,
70
82
  });
71
83
  });
72
84
  });
@@ -120,9 +132,9 @@ describe("PACKAGE_JSON", () => {
120
132
  expect(pkg.devDependencies["@playwright/test"]).toBeDefined();
121
133
  });
122
134
 
123
- it("includes @bascik/bascik as a dependency", () => {
135
+ it("uses the current Bascik prerelease dependency", () => {
124
136
  const pkg = JSON.parse(PACKAGE_JSON("my-app"));
125
- expect(pkg.dependencies["@bascik/bascik"]).toBeDefined();
137
+ expect(pkg.dependencies["@bascik/bascik"]).toBe("^1.0.0-rc.4");
126
138
  });
127
139
 
128
140
  it("uses the supplied name", () => {
@@ -274,6 +286,7 @@ describe("SITE_FOOTER_HTML", () => {
274
286
 
275
287
  it("uses a build-time script for the year", () => {
276
288
  expect(SITE_FOOTER_HTML).toContain("data-bascik-build");
289
+ expect(SITE_FOOTER_HTML).toContain("export default () =>");
277
290
  expect(SITE_FOOTER_HTML).toContain("getFullYear");
278
291
  });
279
292
  });
package/src/scaffold.ts CHANGED
@@ -32,10 +32,10 @@ export const PACKAGE_JSON = (name: string): string =>
32
32
  e2e: "playwright test --config e2e/playwright.config.ts",
33
33
  },
34
34
  dependencies: {
35
- "@bascik/bascik": "^1.0.0-rc.2",
35
+ "@bascik/bascik": "^1.0.0-rc.4",
36
36
  },
37
37
  devDependencies: {
38
- "@bascik/language-server": "^0.1.0-rc.1",
38
+ "@bascik/language-server": "^0.1.0-rc.2",
39
39
  "@playwright/test": "^1.62.0",
40
40
  "@types/node": "^24.0.0",
41
41
  "@vitest/coverage-v8": "^4.1.10",
@@ -325,7 +325,7 @@ export const FAVICON_32_PNG_B64 = `iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0A
325
325
 
326
326
  export const APPLE_TOUCH_ICON_PNG_B64 = `iVBORw0KGgoAAAANSUhEUgAAALQAAAC0CAYAAAA9zQYyAAAACXBIWXMAAAsTAAALEwEAmpwYAAAGKUlEQVR4nO2bzWqdVRSGN72AtqO0WicWJ1KtoTi0dGjTK8gFGGtnH1ghJpXOw1l0kHoTUvlIU6rQa9Dg2R9W8OcK2guwky0nkIGU0kTfsvZZ63nhnWWwz7ufnTwcSCmEEEIIIYQQQgghhBBCCCGEEEII6SG12Xptsyd0Fm2DrZIxtdlYmzVq0TYYSra0dvfUvM2edTA+bdoNpnZvtWTL4kMDU7zHNG+z54tfViVbFn+WvMen9iY2GEvG4M9hH9RQsgV/jtsJf/a/BGqSDfBnYGrBNsCfO7gE2mQb4M8AZWE2wJ87uARqkg3wZ2CK9pjGkjF8/xy2Q8kWvn+O24nvn/0vgZpkA/wZmKI9prFkDP4ctkPJFvw5bif82f8SqEk2wJ+BKdpjGkvG4M9hO5RswZ/jdsKf/S+BmmQD/BmYoj2msWQM/hy2Q8kW/DluJ/zZ/xKoSTbAn4Ep2mMaS8bgz2E7lGzBn+N2wp/9L4GaZAP8GZiiPaaxZAz+HLZDyRb8OW4n/Nn/EqhJNsCfgSnaYxpLxuDPYTuUbMGf43bCn5evj3/farsPNw77+I9t9/PUToo/L1n3ft1sn3z6YTt95uy/evX65bb3dNP9fNW/+POy9Lufvmzn3l55Ceajnr+w0h4c3HY/Z/Ut/rwM/fnvnfb+6ruvhPmol65cbAcvdtzPW52KPy9Jd/c+ey3MR72/v+F+3upQ/HmJenN77dhAf3Fnzf281af487J0/da1YwO9+Fnv81af4s8dXAJAN81G+HMHoAK0SfbBnzuAFKBNuRH+7A0pQJtyI/zZG1KANtk++HMHkAK0SbbBnzsAFKBNuQ/+7A0oQJtyH/zZG1CANtk2+HMHgAK0SXbBnzuAE6BNuQ3+7A0nQJtyG/zZG04ANtku+HMHcAK0STbBnzsAE6BNuQv+7A0mQJtyF/zZG0yANtkm+HMHYAK0SfbAnzuAEqBNuQn+7A0lQJtyE/zZG0qANtke+HMHUAK0SbbAnzsAEgBNuQf+7A0kQJtyD/zZG0iANtkW+HMHQAK0SXbAnzuAEaBNuQX+7A0jQJtyC/zZG0aANtkO+HMHMAK0STbAnzsAEQBNuQP+7A0iQJtyB/zZG0SANtkG+HMHIAK0ZgP8uQMIAdqUG+DP3hACtCk3wJ+9IQRok22AP3cAIUCbZAP8uQMAAdqUG+DP3gACtCk3wJ+9AQRok22AP3cAIECbZAP8uQP4ANqUG+DP3vABtCk3wJ+94QNok22AP3cAH0CbZAP8uQPwANqUG+DP3uABtCk3wJ+9wQNok22AP3cAHkCbZAP8uQPoANqUG+DP3tABtCk3wJ+9oQNok22AP3cAHUCbZAP8uQPgANqUG+DP3sABtCk3wJ+9gQNok22AP3cAHECbZAP8uQPYANqUG+DP3rABtCk3wJ+9YQNok22AP3cAG0CbZAP8uQPQANqUG+DP3qABtCk3wJ+9QQNok22AP3cAGkCbZAP8uQPIANqUG+DPmXpze62dPnP2WL31zQ3389aTF3/O1N2HG8cG+ttHn7uft56w+HOyHrzYaZeuXHwtzB98/N7hz3qft56g+HPSPji43c5fWHklzG+9c659/8tX7uesJy/+nLV7Tzfb1euXX4L52o2P2v5vX7ufr/634s/Z+8Of2+3+/sZhf/zrjvt56v8o/tzBJVCTbIA/A1O0xzSWjFl88A7Gp02+Af4MWHEe1tTurZZsWXxo7+GpyTfAnwEr2sMaS8bgz2E7lGxp7e6peZs962B82rQb4M9AFeZRzdvs+eKXVcmWxZ8l7/GpvYnW/d2iwC0CBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIECBAgAIAAAAAAAAgAAAAAAQIEC`;
327
327
 
328
- /** Head component — charset, viewport, favicon, CSS link. */
328
+ /** Head component: charset, viewport, favicon, CSS link. */
329
329
  export const SITE_META_HTML = `<meta charset="UTF-8" />
330
330
  <meta name="viewport" content="width=device-width, initial-scale=1.0" />
331
331
  <meta name="description" content="A fast, modern website built with Bascik." />
@@ -491,7 +491,7 @@ ${SITE_FOOTER_CSS}</style>
491
491
  <div class="container">
492
492
  <div class="footer-inner">
493
493
  <span class="footer-copy">
494
- &copy; <script data-bascik-build>console.log(new Date().getFullYear())</script>
494
+ &copy; <script data-bascik-build>export default () => String(new Date().getFullYear());</script>
495
495
  <span data-bascik-prop-brand>My Site</span>
496
496
  </span>
497
497
  <nav class="footer-nav" aria-label="Footer navigation">
@@ -583,7 +583,7 @@ export const MY_COUNTER_CSS = `.counter {
583
583
  }
584
584
  `;
585
585
 
586
- /** Interactive counter — two instances on a page stay fully independent. */
586
+ /** Interactive counter: two instances on a page stay fully independent. */
587
587
  export const MY_COUNTER_HTML = `<style>
588
588
  ${MY_COUNTER_CSS}</style>
589
589
  <div class="counter">
@@ -718,7 +718,7 @@ ${body}
718
718
  `;
719
719
 
720
720
  export const indexPage = (brand: string): string =>
721
- pageShell(`Home — ${brand}`, brand, ` <section class="hero">
721
+ pageShell(`Home - ${brand}`, brand, ` <section class="hero">
722
722
  <div class="container">
723
723
  <p class="section-label">Welcome</p>
724
724
  <h1>Build fast with <span>${brand}</span></h1>
@@ -762,7 +762,7 @@ export const indexPage = (brand: string): string =>
762
762
  <div class="container">
763
763
  <p class="section-label">Interactive</p>
764
764
  <h2>Counter component</h2>
765
- <p>Each instance holds its own state. Place the same component twice — they stay independent.</p>
765
+ <p>Each instance holds its own state. Place the same component twice, and they stay independent.</p>
766
766
  <div style="display:flex;gap:48px;flex-wrap:wrap;margin-top:24px;">
767
767
  <my-counter />
768
768
  <my-counter />
@@ -771,7 +771,7 @@ export const indexPage = (brand: string): string =>
771
771
  </section>`);
772
772
 
773
773
  export const aboutPage = (brand: string): string =>
774
- pageShell(`About — ${brand}`, brand, ` <div class="container">
774
+ pageShell(`About - ${brand}`, brand, ` <div class="container">
775
775
  <section class="section">
776
776
  <p class="section-label">About</p>
777
777
  <h1>About us</h1>
@@ -795,7 +795,7 @@ export const aboutPage = (brand: string): string =>
795
795
  </div>`);
796
796
 
797
797
  export const contactPage = (brand: string): string =>
798
- pageShell(`Contact — ${brand}`, brand, ` <div class="container">
798
+ pageShell(`Contact - ${brand}`, brand, ` <div class="container">
799
799
  <section class="section">
800
800
  <p class="section-label">Contact</p>
801
801
  <h1>Get in touch</h1>
@@ -820,7 +820,7 @@ export const contactPage = (brand: string): string =>
820
820
  </div>`);
821
821
 
822
822
  export const notFoundPage = (brand: string): string =>
823
- pageShell(`404 — ${brand}`, brand, ` <div class="container">
823
+ pageShell(`404 - ${brand}`, brand, ` <div class="container">
824
824
  <section class="section" style="text-align:center;">
825
825
  <p class="section-label">Error</p>
826
826
  <h1 style="font-size:clamp(4rem,12vw,8rem);color:var(--accent);margin-bottom:8px;">404</h1>