create-bascik 1.0.0-rc.7 → 1.0.0-rc.9
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/README.md +2 -2
- package/assets/skills/README.md +1 -1
- package/assets/skills/bascik/SKILL.md +12 -11
- package/assets/skills/bascik-config/SKILL.md +1 -1
- package/assets/skills/bascik-scripts/SKILL.md +10 -9
- package/assets/skills/bascik-seo/SKILL.md +1 -1
- package/dist/scaffold.d.ts +2 -2
- package/dist/scaffold.js +5 -5
- package/package.json +1 -1
- package/src/scaffold.test.ts +2 -2
- package/src/scaffold.ts +5 -5
package/README.md
CHANGED
|
@@ -10,13 +10,13 @@ Interactive project initializer for [Bascik](https://bascik.dev).
|
|
|
10
10
|
Scaffold a new Bascik project in one command:
|
|
11
11
|
|
|
12
12
|
```sh
|
|
13
|
-
npm create bascik@
|
|
13
|
+
npm create bascik@rc my-site -y
|
|
14
14
|
```
|
|
15
15
|
|
|
16
16
|
Pass a project name as an argument or run without arguments to be prompted:
|
|
17
17
|
|
|
18
18
|
```sh
|
|
19
|
-
npm create bascik@
|
|
19
|
+
npm create bascik@rc
|
|
20
20
|
```
|
|
21
21
|
|
|
22
22
|
## What it scaffolds
|
package/assets/skills/README.md
CHANGED
|
@@ -18,7 +18,7 @@ Without it, download every skill into the shared `.agents/skills/` folder (use `
|
|
|
18
18
|
mkdir -p .agents/skills && curl -fsSL https://bascik.dev/assets/skills.tar.gz | tar -xz -C .agents/skills
|
|
19
19
|
```
|
|
20
20
|
|
|
21
|
-
New projects created with `npm create bascik@
|
|
21
|
+
New projects created with `npm create bascik@rc` include these skills already.
|
|
22
22
|
|
|
23
23
|
## Skills
|
|
24
24
|
|
|
@@ -88,7 +88,7 @@ New components are picked up by the running dev server automatically; no restart
|
|
|
88
88
|
9. **Never write generated files into `src/`.** Generated output goes to `dist/` (`BASCIK_OUT_DIR`) from an existing `pipeline.exec` script.
|
|
89
89
|
10. **Client scripts are not bundled.** Bare imports such as `import x from 'pkg'` fail in the browser. They work only in Node-side scripts (`data-bascik-build`, `data-bascik-server`).
|
|
90
90
|
11. **Extract shared layout.** Repeated `<head>` tags, headers, and footers become components (`<site-head />`, `<site-header />`, `<site-footer />`). Pages keep their own `<html>`, `<head>`, and `<body>`.
|
|
91
|
-
12. **Script directives are exclusive.** One `<script>` tag takes at most one of `data-bascik-build`, `data-bascik-routes`, `data-bascik-server`, `data-bascik-stream`.
|
|
91
|
+
12. **Script directives are exclusive.** One `<script>` tag takes at most one of `data-bascik-build`, `data-bascik-build-once`, `data-bascik-routes`, `data-bascik-server`, `data-bascik-stream`. `data-bascik-build-once` runs once per file with no page context.
|
|
92
92
|
|
|
93
93
|
## Slots, props, and attributes
|
|
94
94
|
|
|
@@ -104,14 +104,15 @@ Pass props as `data-bascik-prop-<name>="..."` on the usage tag. A plain attribut
|
|
|
104
104
|
|
|
105
105
|
## Script tags at a glance
|
|
106
106
|
|
|
107
|
-
| Tag
|
|
108
|
-
|
|
|
109
|
-
| `<script>`
|
|
110
|
-
| `<script data-bascik-build>`
|
|
111
|
-
| `<script data-bascik-
|
|
112
|
-
| `<script data-bascik-
|
|
113
|
-
| `<script data-bascik-
|
|
114
|
-
| `<script data-bascik-
|
|
107
|
+
| Tag | Runs | Use for |
|
|
108
|
+
| --------------------------------- | --------------------------- | ---------------------------------------------------------------------- |
|
|
109
|
+
| `<script>` | Browser | Component and page behavior (scoped, IIFE-wrapped) |
|
|
110
|
+
| `<script data-bascik-build>` | Node, at build time | Markup from files, Markdown, APIs; output replaces the tag |
|
|
111
|
+
| `<script data-bascik-build-once>` | Node, once per file | Markup shared by every use of a component or every route of a template |
|
|
112
|
+
| `<script data-bascik-routes>` | Node, at build time | Expanding `[slug].html` templates into many pages |
|
|
113
|
+
| `<script data-bascik-server>` | Node, per request | Personalized markup (needs `bascik --server` or a serverless target) |
|
|
114
|
+
| `<script data-bascik-stream>` | Node, per request, streamed | Slow per-request blocks that should not delay the page shell |
|
|
115
|
+
| `<script data-bascik-dev>` | Browser, dev only | Debug helpers removed from production builds |
|
|
115
116
|
|
|
116
117
|
Node-side scripts export a default function that returns an HTML string. Details are in the `bascik-scripts` skill.
|
|
117
118
|
|
|
@@ -131,7 +132,7 @@ npx @bascik/language-server --check # the per-file checks on their own (bascik
|
|
|
131
132
|
|
|
132
133
|
`bascik --check` and the linter are not two overlapping tools. `bascik --check` answers project questions (duplicate routes, unused components, config), and the linter answers per-file questions (attribute typos, CSS that cannot be scoped). `bascik --check` runs the linter too, so use it, and reach for the linter alone only on a folder outside the project.
|
|
133
134
|
|
|
134
|
-
New project: `npm create bascik@
|
|
135
|
+
New project: `npm create bascik@rc my-site -y`. Production builds need a site URL for `sitemap.xml` and `robots.txt`: `BASCIK_SITE_URL=https://example.com bascik --build` (or `--site-url`, or `.env`). Full CLI and error output: `read-doc.mjs /cli`.
|
|
135
136
|
|
|
136
137
|
## Verify every change
|
|
137
138
|
|
|
@@ -195,7 +196,7 @@ The script downloads from bascik.dev, maps files to the project's configured fol
|
|
|
195
196
|
|
|
196
197
|
## Build a new site
|
|
197
198
|
|
|
198
|
-
1. `npm create bascik@
|
|
199
|
+
1. `npm create bascik@rc my-site -y`, or read the existing project first with `list-components.mjs`. Add `BASCIK_SITE_URL=https://example.com` to `.env`.
|
|
199
200
|
2. Plan the pages and URLs (one purpose and one `<h1>` each), the shared layout (head, header, footer), repeated content (a collection in `content/`), and anything that needs a server (API routes, per-request markup). Ask when the plan is unclear.
|
|
200
201
|
3. Copy the foundation: `add-example.mjs starter-site`, or only the parts you need. Then replace the example name, navigation (`src/components/site-header/links.ts`), colors (tokens in `src/pages/css/styles.css`), and every page's `seo({ title, description })`.
|
|
201
202
|
4. Add pages, components (`new-component.mjs`), and interactive examples. Load the companion skill for each area.
|
|
@@ -113,7 +113,7 @@ export default defineConfig({
|
|
|
113
113
|
1. **One site, one project:** each site has its own config, `src/pages/`, `.env`, and `dist/`. Run Bascik from the site folder.
|
|
114
114
|
2. **Tag names are unique across all of a site's roots.** Roots never nest or repeat, including through symlinks.
|
|
115
115
|
3. **List the site's own folder first.** `bascik add` and `add-example.mjs` write into the first root.
|
|
116
|
-
4. **Shared components import shared helpers with relative paths** (`../../scripts/links.ts`) unless every site points `scripts.importRoot` at the same folder. `@/` resolves against the consuming site, and a broken import fails that site's build even when no page uses the component.
|
|
116
|
+
4. **Shared components import shared helpers with relative paths** (`../../scripts/links.ts`) unless every site points `scripts.importRoot` at the same folder. `@/` resolves against the consuming site, and a broken import fails that site's build: a `data-bascik-build-once` script fails it even when no page uses the component, and a `data-bascik-build` script fails it on the first page that does.
|
|
117
117
|
5. **Watch shared helper and style folders** with `pipeline.watchPaths`; only component roots are watched automatically.
|
|
118
118
|
6. **Share tokens as a stylesheet:** `assets: { inlineStyles: ['../../shared/styles/tokens.css'] }`.
|
|
119
119
|
7. **One Bascik version** for every site. Task runners (Turborepo, Nx) must list `shared/**` as a build input and `BASCIK_SITE_URL` as an env input.
|
|
@@ -13,20 +13,21 @@ Bascik runs Node.js code inside special `<script>` tags. Each is a standard ESM
|
|
|
13
13
|
|
|
14
14
|
## Pick the right tag
|
|
15
15
|
|
|
16
|
-
| Need
|
|
17
|
-
|
|
|
18
|
-
|
|
|
19
|
-
|
|
|
20
|
-
|
|
|
21
|
-
|
|
|
22
|
-
| A
|
|
16
|
+
| Need | Tag | Runs |
|
|
17
|
+
| ---------------------------------------------------------------- | ------------------------------------------------ | ---------------------------------------------------------------- |
|
|
18
|
+
| Page-specific or per-component-use HTML | `data-bascik-build` | Build and dev, once per use (cached by inputs) |
|
|
19
|
+
| Identical HTML shared by every use of a component or every route | `data-bascik-build-once` | Once per file (component registration or route template), cached |
|
|
20
|
+
| Many pages from one `[param].html` template | `data-bascik-routes` | Build and dev startup |
|
|
21
|
+
| Markup that depends on the request (cookies, headers, query) | `data-bascik-server` | Every request, before the response starts |
|
|
22
|
+
| A slow per-request block that should not delay the page shell | `data-bascik-stream` | Every request, streamed in document order |
|
|
23
|
+
| A JSON or HTTP endpoint | `src/api/<name>.ts` exporting `GET`, `POST`, ... | Every request |
|
|
23
24
|
|
|
24
25
|
Prefer build-time work. Server, stream, and API code needs `bascik --server` (or a serverless target such as Cloudflare); a plain static host cannot run it.
|
|
25
26
|
|
|
26
27
|
## Rules for every Node-side script
|
|
27
28
|
|
|
28
29
|
1. **Export a default function that returns an HTML string.** `console.log()` output goes to the terminal only; it never becomes markup.
|
|
29
|
-
2. **One directive per tag.** Combining `data-bascik-build`, `-routes`, `-server`, or `-stream` is a hard error.
|
|
30
|
+
2. **One directive per tag.** Combining `data-bascik-build`, `data-bascik-build-once`, `-routes`, `-server`, or `-stream` is a hard error.
|
|
30
31
|
3. **Imports:** top-level `import` and `await` work. Use `@/` for shared helpers: `import { renderMd } from '@/lib/md.ts'` resolves against `scripts.importRoot` (default `src`) from any depth. `./` and `../` resolve against the file containing the script. Bare npm packages work.
|
|
31
32
|
4. **Never import with a bare leading `/`.** `import x from '/lib/x.ts'` and `src="/lib/x.ts"` are hard errors. Use `@/lib/x.ts` or `./lib/x.ts`.
|
|
32
33
|
5. **Helpers import helpers with relative paths.** `@/` is rewritten only inside script tags; a `.ts` helper that imports another helper uses `./other.ts` (with the extension) and `import type` for type-only imports. tsconfig `paths` do not apply.
|
|
@@ -53,7 +54,7 @@ Prefer build-time work. Server, stream, and API code needs `bascik --server` (or
|
|
|
53
54
|
- Each uncached script runs in its own child process (about 50 to 150 ms). Output is cached on disk in `node_modules/.cache/bascik/script-cache/`.
|
|
54
55
|
- **Cache gotcha:** the cache key covers the script, its statically referenced local files, installed package versions, the page path, site URL, and base. It cannot see network responses, `readdir` results, or computed paths. Exclude scripts that read remote or directory data with `scripts.cache.exclude`, and declare environment variables a script reads in `scripts.cache.environment`.
|
|
55
56
|
- **Fetch once:** read data once per page in one script, then feed it to the parts that need it, instead of fetching per component instance.
|
|
56
|
-
- **Component
|
|
57
|
+
- **Component context:** `data-bascik-build` runs for each use with that page's context. Use `data-bascik-build-once` for identical output with no page or route context: in a component every use shares one run, and in a dynamic route template every route shares one run. Its output is cached like any build script. Use a build-wide `pre` exec script when work should be shared across different files.
|
|
57
58
|
- Errors follow `scripts.onBuildScriptError` (`'error'` by default): dev shows the error on that page and keeps serving; `--build` fails.
|
|
58
59
|
|
|
59
60
|
Per-page values come from environment variables: `BASCIK_PAGE_PATH` (`/about`), `BASCIK_PAGE_FILE`, `BASCIK_SOURCE_FILE`, `BASCIK_PAGES_DIR`, `BASCIK_BUILD` (`"1"` in `--build`), `BASCIK_SITE_URL` (absent when unset), `BASCIK_BASE`, and `BASCIK_ROUTE` (dynamic routes). Details: `read-doc.mjs /build-scripts`, `/environment-variables`, `/how-to/page-aware-scripts`.
|
|
@@ -24,7 +24,7 @@ Bascik outputs static HTML, so search engines and AI crawlers read every word wi
|
|
|
24
24
|
|
|
25
25
|
## Rules (must follow)
|
|
26
26
|
|
|
27
|
-
1. **
|
|
27
|
+
1. **Build tags in the context of the page that uses them.** A build script inside a component runs for each use, with that page's environment variables; no page-aware marker is needed.
|
|
28
28
|
2. **Never hard-code the domain.** URLs come from `BASCIK_SITE_URL` (CI variable or `.env`), `BASCIK_BASE`, and `BASCIK_PAGE_PATH`. `bascik --build` fails without the site URL because it writes the sitemap and robots.txt.
|
|
29
29
|
3. **Escape every value** you interpolate into a tag. For JSON-LD, `JSON.stringify` and replace `<` with `\u003c` so data can never close the script tag. Describe only what is visible on the page.
|
|
30
30
|
4. **Dynamic routes** read the item from `BASCIK_ROUTE` in the head build script and pass its fields to the tag helper.
|
package/dist/scaffold.d.ts
CHANGED
|
@@ -30,7 +30,7 @@ export declare const SITE_HEADER_CSS = ".header {\n position: sticky;\n top: 0
|
|
|
30
30
|
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";
|
|
31
31
|
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";
|
|
32
32
|
/** Footer with brand prop and build-time year. */
|
|
33
|
-
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 © <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";
|
|
33
|
+
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 © <script data-bascik-build-once>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";
|
|
34
34
|
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";
|
|
35
35
|
/**
|
|
36
36
|
* Card component demonstrating named slots.
|
|
@@ -42,7 +42,7 @@ export declare const MY_COUNTER_CSS = ".counter {\n display: inline-flex;\n al
|
|
|
42
42
|
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";
|
|
43
43
|
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";
|
|
44
44
|
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";
|
|
45
|
-
export declare const SITE_FOOTER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-footer component', () => {\n const path = join(process.cwd(), 'src/components/site-footer/site-footer.html');\n\n it('renders brand prop placeholder and build-
|
|
45
|
+
export declare const SITE_FOOTER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('site-footer component', () => {\n const path = join(process.cwd(), 'src/components/site-footer/site-footer.html');\n\n it('renders brand prop placeholder and build-once year script', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('data-bascik-prop-brand');\n expect(html).toContain('data-bascik-build-once');\n expect(html).toContain('getFullYear');\n });\n});\n";
|
|
46
46
|
export declare const FEAT_CARD_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('feat-card component', () => {\n const path = join(process.cwd(), 'src/components/feat-card/feat-card.html');\n\n it('defines header, default, and footer slots', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('data-bascik-slot=\"header\"');\n expect(html).toContain('data-bascik-slot');\n expect(html).toContain('data-bascik-slot=\"footer\"');\n });\n});\n";
|
|
47
47
|
export declare const MY_COUNTER_TEST = "import { describe, it, expect } from 'vitest';\nimport { readFile } from 'node:fs/promises';\nimport { join } from 'node:path';\n\ndescribe('my-counter component', () => {\n const path = join(process.cwd(), 'src/components/my-counter/my-counter.html');\n\n it('renders increment and decrement controls with scoped script', async () => {\n const html = await readFile(path, 'utf8');\n expect(html).toContain('id=\"btn-dec\"');\n expect(html).toContain('id=\"btn-inc\"');\n expect(html).toContain('id=\"count-val\"');\n expect(html).toContain('addEventListener');\n });\n});\n";
|
|
48
48
|
export declare const indexPage: (brand: string) => string;
|
package/dist/scaffold.js
CHANGED
|
@@ -37,10 +37,10 @@ export const PACKAGE_JSON = (name) => JSON.stringify({
|
|
|
37
37
|
e2e: "playwright test --config e2e/playwright.config.ts",
|
|
38
38
|
},
|
|
39
39
|
dependencies: {
|
|
40
|
-
"@bascik/bascik": "^1.0.0-rc.
|
|
40
|
+
"@bascik/bascik": "^1.0.0-rc.9",
|
|
41
41
|
},
|
|
42
42
|
devDependencies: {
|
|
43
|
-
"@bascik/language-server": "^0.1.0-rc.
|
|
43
|
+
"@bascik/language-server": "^0.1.0-rc.5",
|
|
44
44
|
"@playwright/test": "^1.62.0",
|
|
45
45
|
"@types/node": "^24.0.0",
|
|
46
46
|
"@vitest/coverage-v8": "^4.1.10",
|
|
@@ -465,7 +465,7 @@ ${SITE_FOOTER_CSS}</style>
|
|
|
465
465
|
<div class="container">
|
|
466
466
|
<div class="footer-inner">
|
|
467
467
|
<span class="footer-copy">
|
|
468
|
-
© <script data-bascik-build>export default () => String(new Date().getFullYear());</script>
|
|
468
|
+
© <script data-bascik-build-once>export default () => String(new Date().getFullYear());</script>
|
|
469
469
|
<span data-bascik-prop-brand>My Site</span>
|
|
470
470
|
</span>
|
|
471
471
|
<nav class="footer-nav" aria-label="Footer navigation">
|
|
@@ -619,10 +619,10 @@ import { join } from 'node:path';
|
|
|
619
619
|
describe('site-footer component', () => {
|
|
620
620
|
const path = join(process.cwd(), 'src/components/site-footer/site-footer.html');
|
|
621
621
|
|
|
622
|
-
it('renders brand prop placeholder and build-
|
|
622
|
+
it('renders brand prop placeholder and build-once year script', async () => {
|
|
623
623
|
const html = await readFile(path, 'utf8');
|
|
624
624
|
expect(html).toContain('data-bascik-prop-brand');
|
|
625
|
-
expect(html).toContain('data-bascik-build');
|
|
625
|
+
expect(html).toContain('data-bascik-build-once');
|
|
626
626
|
expect(html).toContain('getFullYear');
|
|
627
627
|
});
|
|
628
628
|
});
|
package/package.json
CHANGED
package/src/scaffold.test.ts
CHANGED
|
@@ -336,8 +336,8 @@ describe("SITE_FOOTER_HTML", () => {
|
|
|
336
336
|
expect(SITE_FOOTER_HTML).not.toContain("${SITE_FOOTER_CSS}");
|
|
337
337
|
});
|
|
338
338
|
|
|
339
|
-
it("uses a build-
|
|
340
|
-
expect(SITE_FOOTER_HTML).toContain("data-bascik-build");
|
|
339
|
+
it("uses a build-once script for the year", () => {
|
|
340
|
+
expect(SITE_FOOTER_HTML).toContain("<script data-bascik-build-once>");
|
|
341
341
|
expect(SITE_FOOTER_HTML).toContain("export default () =>");
|
|
342
342
|
expect(SITE_FOOTER_HTML).toContain("getFullYear");
|
|
343
343
|
});
|
package/src/scaffold.ts
CHANGED
|
@@ -48,10 +48,10 @@ export const PACKAGE_JSON = (name: string): string =>
|
|
|
48
48
|
e2e: "playwright test --config e2e/playwright.config.ts",
|
|
49
49
|
},
|
|
50
50
|
dependencies: {
|
|
51
|
-
"@bascik/bascik": "^1.0.0-rc.
|
|
51
|
+
"@bascik/bascik": "^1.0.0-rc.9",
|
|
52
52
|
},
|
|
53
53
|
devDependencies: {
|
|
54
|
-
"@bascik/language-server": "^0.1.0-rc.
|
|
54
|
+
"@bascik/language-server": "^0.1.0-rc.5",
|
|
55
55
|
"@playwright/test": "^1.62.0",
|
|
56
56
|
"@types/node": "^24.0.0",
|
|
57
57
|
"@vitest/coverage-v8": "^4.1.10",
|
|
@@ -507,7 +507,7 @@ ${SITE_FOOTER_CSS}</style>
|
|
|
507
507
|
<div class="container">
|
|
508
508
|
<div class="footer-inner">
|
|
509
509
|
<span class="footer-copy">
|
|
510
|
-
© <script data-bascik-build>export default () => String(new Date().getFullYear());</script>
|
|
510
|
+
© <script data-bascik-build-once>export default () => String(new Date().getFullYear());</script>
|
|
511
511
|
<span data-bascik-prop-brand>My Site</span>
|
|
512
512
|
</span>
|
|
513
513
|
<nav class="footer-nav" aria-label="Footer navigation">
|
|
@@ -669,10 +669,10 @@ import { join } from 'node:path';
|
|
|
669
669
|
describe('site-footer component', () => {
|
|
670
670
|
const path = join(process.cwd(), 'src/components/site-footer/site-footer.html');
|
|
671
671
|
|
|
672
|
-
it('renders brand prop placeholder and build-
|
|
672
|
+
it('renders brand prop placeholder and build-once year script', async () => {
|
|
673
673
|
const html = await readFile(path, 'utf8');
|
|
674
674
|
expect(html).toContain('data-bascik-prop-brand');
|
|
675
|
-
expect(html).toContain('data-bascik-build');
|
|
675
|
+
expect(html).toContain('data-bascik-build-once');
|
|
676
676
|
expect(html).toContain('getFullYear');
|
|
677
677
|
});
|
|
678
678
|
});
|