@twobirds/microcomponents 0.13.0 → 0.13.2

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 CHANGED
@@ -138,7 +138,7 @@ Once installed from npm the same command is available as `mc` (and as `microcomp
138
138
 
139
139
  ```bash
140
140
  mc new my-card [--css] [--html] [--target-html] [--shadow-html] [--target-css] [--shadow-css] [--js] [--dir <path>] [--force] [--dry-run]
141
- mc create --namespace <ns> [--dir <path>] [--force] [--dry-run]
141
+ mc create --namespace <ns> [--dir <path>] [--force] [--yes] [--dry-run]
142
142
  mc dev [--port <n>] [--host <addr>]
143
143
  mc bundle
144
144
  mc preview [--port <n>] [--host <addr>]
@@ -165,7 +165,7 @@ npx -p @twobirds/microcomponents mc new my-card --target-html --target-css
165
165
  npx -p @twobirds/microcomponents microcomponents-mcp
166
166
  ```
167
167
 
168
- The scaffold derives the file path from the tag using the same rule the runtime autoload loader uses (`my-card` → `my/card.ts`), names companion assets after the runtime's asset descriptors (`targetHtml` → `card.target.html`), and wires them into a `static assets` declaration. `--js` emits a plain JavaScript file for the rapid-prototyping path.
168
+ The scaffold derives the file path from the tag using the same rule the runtime autoload loader uses (`my-card` → `my/card.ts`), names companion assets after the runtime's asset descriptors (`targetHtml` → `card.target.html`), and wires them into a `static assets` declaration. Every hyphen becomes a folder, so the tag's segments are the DOM containment chain — `<cart-container-articles>` sits inside `<cart-container>`, so it is `src/cart/container/articles.ts` — and the last segment names what the component does, not what shape it is: `panel`, `box`, `wrapper` are superfluous, because a folder level costs a segment that has to say something. `--js` emits a plain JavaScript file for the rapid-prototyping path.
169
169
 
170
170
  ## Examples
171
171
 
@@ -414,7 +414,7 @@ Feel free to contact me for any questions or inquiries:
414
414
 
415
415
  email [Frank Thürigen](mailto:fthuerigen@gmail.com?subject=Micro%20Components)
416
416
 
417
- You can [fund this](https://www.gofundme.com/f/nachhaltige-programmierung).
417
+ You can [fund this](https://www.paypal.com/cgi-bin/webscr?cmd=_donations&business=fthuerigen@gmail.com&item_name=Micro+Components&currency_code=EUR).
418
418
 
419
419
  ## Disclaimer
420
420
 
@@ -18,12 +18,38 @@ export type Runner = (script: string, options?: {
18
18
  /** Asks the user to confirm a namespace long enough to be worth a second look. */
19
19
  export type Confirmer = (namespace: string) => Promise<boolean>;
20
20
  /**
21
+ Whether `mc <command>` runs the project's package.json script or the CLI's own
22
+ implementation.
23
+
24
+ A script that is a thin wrapper around the same command must not be delegated:
25
+ `mc dev` would spawn `pnpm run dev`, which runs `mc dev`, which spawns
26
+ `pnpm run dev`, forever. The scaffold writes exactly that shape (`"dev": "mc dev"`,
27
+ `"preview": "mc preview"`), so this guard is what keeps `pnpm dev` in a scaffolded
28
+ project from looping.
29
+
30
+ @example
31
+ ```ts
32
+ scriptTarget('mc dev', 'dev'); // 'self'
33
+ scriptTarget('mc dev --port 5173', 'dev'); // 'self'
34
+ scriptTarget('npx mc preview', 'preview'); // 'self'
35
+ scriptTarget('vite preview --outDir build', 'dev'); // 'delegate'
36
+ scriptTarget('tsc -p tsconfig.json && mc bundle', 'bundle'); // 'delegate'
37
+ scriptTarget(undefined, 'clean'); // 'self'
38
+ ```
39
+ @param body - The script body, or `undefined` when the project declares no such script.
40
+ @param command - The `mc` command being run.
41
+ @returns `'delegate'` to run the project script, `'self'` to run the CLI's implementation.
42
+ */
43
+ export declare function scriptTarget(body: string | undefined, command: string): 'delegate' | 'self';
44
+ /**
21
45
  Runs a package.json script through pnpm and resolves with its exit code.
22
46
 
23
47
  Thin wrappers like `clean` and `check` delegate here so the script — not the
24
48
  CLI — remains the single source of truth for what each command does. If the
25
49
  current project defines no such script, the CLI runs its own implementation
26
- against the current project, so the command works in any consumer project.
50
+ against the current project, so the command works in any consumer project. A script
51
+ that is a thin wrapper around the command itself is not delegated, because
52
+ delegating makes the CLI run itself forever — see {@link scriptTarget}.
27
53
 
28
54
  @example
29
55
  ```ts
@@ -1 +1 @@
1
- {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/cli/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;EAYE;AAwEF,MAAM,MAAM,MAAM,GAAG,CACpB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACtC,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAyIhE;;;;;;;;;;;;EAYE;AACF,wBAAsB,SAAS,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CAajB;AAED;;;;;;;;;;;;;;;;;;;;EAoBE;AACF,wBAAsB,IAAI,CACzB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAkB,EACvB,OAAO,GAAE,SAA4B,GACnC,OAAO,CAAC,MAAM,CAAC,CA4IjB"}
1
+ {"version":3,"file":"dispatch.d.ts","sourceRoot":"","sources":["../../src/cli/dispatch.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;EAYE;AAwEF,MAAM,MAAM,MAAM,GAAG,CACpB,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,KACtC,OAAO,CAAC,MAAM,CAAC,CAAC;AAErB,kFAAkF;AAClF,MAAM,MAAM,SAAS,GAAG,CAAC,SAAS,EAAE,MAAM,KAAK,OAAO,CAAC,OAAO,CAAC,CAAC;AAgFhE;;;;;;;;;;;;;;;;;;;;;;EAsBE;AACF,wBAAgB,YAAY,CAAC,IAAI,EAAE,MAAM,GAAG,SAAS,EAAE,OAAO,EAAE,MAAM,GAAG,UAAU,GAAG,MAAM,CAQ3F;AAgED;;;;;;;;;;;;;;EAcE;AACF,wBAAsB,SAAS,CAC9B,MAAM,EAAE,MAAM,EACd,OAAO,CAAC,EAAE;IAAE,IAAI,CAAC,EAAE,MAAM,CAAC;IAAC,IAAI,CAAC,EAAE,MAAM,CAAA;CAAE,GACxC,OAAO,CAAC,MAAM,CAAC,CAajB;AAED;;;;;;;;;;;;;;;;;;;;EAoBE;AACF,wBAAsB,IAAI,CACzB,IAAI,EAAE,MAAM,EAAE,EACd,GAAG,GAAE,MAAkB,EACvB,OAAO,GAAE,SAA4B,GACnC,OAAO,CAAC,MAAM,CAAC,CA4IjB"}
package/dist/cli/index.js CHANGED
@@ -267,7 +267,7 @@ function packageJson(namespace) {
267
267
  typecheck: "tsc --noEmit -p tsconfig.json && tsc --noEmit -p tsconfig.test.json",
268
268
  test: "vitest run"
269
269
  },
270
- dependencies: { "@twobirds/microcomponents": "^0.13.0" },
270
+ dependencies: { "@twobirds/microcomponents": "^0.13.2" },
271
271
  devDependencies: {
272
272
  typescript: "^6.0.3",
273
273
  vitest: "^4.1.10",
@@ -400,7 +400,9 @@ Never write an import map. \`autoload()\` fetches a component by its tag (\`ns-n
400
400
  npx mc new ${namespace}-name --target-html --target-css
401
401
  \`\`\`
402
402
 
403
- The tag must be kebab-case with at least one hyphen. \`ns-name\` becomes \`src/ns/name.ts\`: the first hyphen becomes the folder, which is the same rule the autoloader uses.
403
+ The tag must be kebab-case with at least one hyphen. Every hyphen becomes a folder, so \`ns-parent-name\` becomes \`src/ns/parent/name.ts\` — the same rule the autoloader uses, so the tag is the file path.
404
+
405
+ The segments are the DOM containment chain: the namespace, then the components the element sits inside, then the component's own name. \`<cart-container-articles>\` sits inside \`<cart-container>\`, so it is \`src/cart/container/articles.ts\`. The last segment names what the component does, not what shape it is: \`<${namespace}-header-search>\` → \`src/${namespace}/header/search.ts\`. Generic structural words (\`panel\`, \`box\`, \`wrapper\`, \`widget\`, \`view\`) are superfluous — \`<${namespace}-header-panel>\` → \`src/${namespace}/header/panel.js\` buys a folder level that says nothing.
404
406
 
405
407
  \`--js\` writes \`src/ns/name.js\` instead of \`.ts\`. \`tsc\` compiles only \`.ts\`, so the dev server and \`mc bundle\` copy a plain \`.js\` component to \`build/\` verbatim — it autoloads and bundles like a compiled component, with no type checking and no \`.d.ts\`.
406
408
 
@@ -1719,23 +1721,56 @@ function spawnProcess(command, args, cwd) {
1719
1721
  });
1720
1722
  }
1721
1723
  /**
1722
- Whether the package.json in the current directory declares `script` in its
1723
- "scripts". Consumers get this check so `mc clean`/`mc check` can fall back to
1724
- the CLI's own implementation when the project defines no such script.
1724
+ The body of a package.json script in the current directory, or `undefined` when the
1725
+ project declares no such script. Consumers get this check so `mc clean`/`mc check`
1726
+ can fall back to the CLI's own implementation when the project defines no such script.
1725
1727
 
1726
1728
  @example
1727
1729
  ```ts
1728
- const ok = await hasScript('clean');
1730
+ await scriptBody('clean'); // 'rm -rf dist'
1731
+ await scriptBody('nope'); // undefined
1729
1732
  ```
1730
1733
  @param script - Script name to look up.
1731
1734
  */
1732
- async function hasScript(script) {
1735
+ async function scriptBody(script) {
1733
1736
  try {
1734
- return typeof JSON.parse(await readFile(path.join(process.cwd(), "package.json"), "utf8"))?.scripts?.[script] === "string";
1737
+ const body = JSON.parse(await readFile(path.join(process.cwd(), "package.json"), "utf8"))?.scripts?.[script];
1738
+ return typeof body === "string" ? body : void 0;
1735
1739
  } catch {
1736
- return false;
1740
+ return;
1737
1741
  }
1738
1742
  }
1743
+ /** The CLI's own bin names, so a script that wraps this command is recognized. */
1744
+ var SELF_BINS = /* @__PURE__ */ new Set(["mc", "microcomponents"]);
1745
+ /**
1746
+ Whether `mc <command>` runs the project's package.json script or the CLI's own
1747
+ implementation.
1748
+
1749
+ A script that is a thin wrapper around the same command must not be delegated:
1750
+ `mc dev` would spawn `pnpm run dev`, which runs `mc dev`, which spawns
1751
+ `pnpm run dev`, forever. The scaffold writes exactly that shape (`"dev": "mc dev"`,
1752
+ `"preview": "mc preview"`), so this guard is what keeps `pnpm dev` in a scaffolded
1753
+ project from looping.
1754
+
1755
+ @example
1756
+ ```ts
1757
+ scriptTarget('mc dev', 'dev'); // 'self'
1758
+ scriptTarget('mc dev --port 5173', 'dev'); // 'self'
1759
+ scriptTarget('npx mc preview', 'preview'); // 'self'
1760
+ scriptTarget('vite preview --outDir build', 'dev'); // 'delegate'
1761
+ scriptTarget('tsc -p tsconfig.json && mc bundle', 'bundle'); // 'delegate'
1762
+ scriptTarget(undefined, 'clean'); // 'self'
1763
+ ```
1764
+ @param body - The script body, or `undefined` when the project declares no such script.
1765
+ @param command - The `mc` command being run.
1766
+ @returns `'delegate'` to run the project script, `'self'` to run the CLI's implementation.
1767
+ */
1768
+ function scriptTarget(body, command) {
1769
+ if (!body) return "self";
1770
+ const tokens = body.trim().split(/\s+/);
1771
+ while (tokens[0] === "npx" || tokens[0] === "pnpm" && tokens[1] === "exec") tokens.splice(0, tokens[0] === "npx" ? 1 : 2);
1772
+ return SELF_BINS.has(tokens[0]) && tokens[1] === command ? "self" : "delegate";
1773
+ }
1739
1774
  /** Build artifacts to remove, relative to the project root. */
1740
1775
  var CLEAN_DIRS = [
1741
1776
  "dist",
@@ -1809,7 +1844,9 @@ Runs a package.json script through pnpm and resolves with its exit code.
1809
1844
  Thin wrappers like `clean` and `check` delegate here so the script — not the
1810
1845
  CLI — remains the single source of truth for what each command does. If the
1811
1846
  current project defines no such script, the CLI runs its own implementation
1812
- against the current project, so the command works in any consumer project.
1847
+ against the current project, so the command works in any consumer project. A script
1848
+ that is a thin wrapper around the command itself is not delegated, because
1849
+ delegating makes the CLI run itself forever — see {@link scriptTarget}.
1813
1850
 
1814
1851
  @example
1815
1852
  ```ts
@@ -1817,7 +1854,7 @@ const code = await runScript('check');
1817
1854
  ```
1818
1855
  */
1819
1856
  async function runScript(script, options) {
1820
- if (await hasScript(script)) return spawnProcess("pnpm", ["run", script]);
1857
+ if (scriptTarget(await scriptBody(script), script) === "delegate") return spawnProcess("pnpm", ["run", script]);
1821
1858
  if (script === "clean") return cleanSelf();
1822
1859
  if (script === "check") return checkSelf();
1823
1860
  if (script === "dev") return runDev(process.cwd(), void 0, options?.port ?? 5173, options?.host ?? "127.0.0.1");
@@ -44,18 +44,25 @@ mc create --namespace shop
44
44
  # project "shop" created in shop/ — run: cd shop && pnpm install && pnpm dev
45
45
  ```
46
46
 
47
- Flags: `--namespace <ns>` (1 to 4 lowercase letters, required), `--dir <path>` (default:
48
- the namespace), `--force`, `--dry-run`.
47
+ Flags: `--namespace <ns>` (lowercase letters and digits, starting with a letter, any
48
+ length, required), `--dir <path>` (default: the namespace), `--force`, `--yes` (`-y`,
49
+ skip the confirmation), `--dry-run`. The project owns its namespace, so length is the
50
+ user's call; past 5 characters `mc create` warns and asks the terminal to confirm before
51
+ anything is written, and `--dry-run` warns without prompting.
49
52
 
50
53
  ## mc dev
51
54
 
52
55
  Runs `tsc -w` plus a `node:http` server that serves `build/`. The page loads components
53
56
  on demand through `autoload()` — nothing is bundled, and a new component is picked up
54
- without restarting.
57
+ without restarting. The server rewrites the `@twobirds/microcomponents` specifier in the
58
+ JS it serves to the `/microcomponents.min.js` it answers, so the page carries no import
59
+ map, and it mirrors the files `tsc` does not compile — companion assets and plain `.js`
60
+ components — into `build/` next to the compiled component.
55
61
 
56
62
  ```sh
57
- mc dev # dev server on port 5173
63
+ mc dev # dev server on port 5173, bound to 127.0.0.1
58
64
  mc dev --port 3000
65
+ mc dev --host 0.0.0.0 # widen the bind to the LAN
59
66
  ```
60
67
 
61
68
  ## mc bundle
@@ -70,11 +77,13 @@ mc bundle
70
77
 
71
78
  ## mc preview
72
79
 
73
- Serves the production site in `public/`.
80
+ Serves the production site in `public/`. Like `mc dev`, it binds to `127.0.0.1` by
81
+ default, so the served files are not reachable off the machine; `--host` widens the bind.
74
82
 
75
83
  ```sh
76
- mc preview # preview server on port 5174
84
+ mc preview # preview server on port 5174, bound to 127.0.0.1
77
85
  mc preview --port 8080
86
+ mc preview --host 0.0.0.0 # widen the bind to the LAN
78
87
  ```
79
88
 
80
89
  ## mc clean
@@ -101,7 +110,7 @@ mc check
101
110
  Reports the version of the installed library, not the version of the project it runs in.
102
111
 
103
112
  ```sh
104
- mc --version # 0.13.0
113
+ mc --version # 0.13.2
105
114
  ```
106
115
 
107
116
  ## mc --help
@@ -1 +1 @@
1
- {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/docs/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiHE"}
1
+ {"version":3,"file":"cli.d.ts","sourceRoot":"","sources":["../../src/docs/cli.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA0HE"}
@@ -14,10 +14,11 @@ the prompt).
14
14
  npx @twobirds/microcomponents create --namespace shop
15
15
  # created shop/package.json
16
16
  # created shop/opencode.jsonc
17
+ # created shop/src/bundle.json
17
18
  # created shop/src/index.html
18
19
  # created shop/src/index-dev.html
19
20
  # created shop/src/shop/app.ts
20
- # created shop/src/shop/app.target.html
21
+ # created shop/src/shop/app.html
21
22
  # created shop/src/shop/app.target.css
22
23
  # created shop/src/shop/bundle.ts
23
24
  # ...
@@ -36,7 +37,11 @@ the project.
36
37
  `mc new` derives the file path from the tag using the same rule the runtime autoloader
37
38
  uses (`shop-app` → `shop/app.ts`), names companion assets after the runtime's asset
38
39
  descriptors (`targetHtml` → `app.target.html`), and wires them into a `static assets`
39
- declaration.
40
+ declaration. Every hyphen becomes a folder, so the segments are the DOM containment
41
+ chain: a child of `<cart-container>` is `<cart-container-articles>` →
42
+ `src/cart/container/articles.ts`. The last segment names what the component does, not
43
+ what shape it is — `panel`, `box`, `wrapper` are superfluous, because a folder level
44
+ costs a segment that has to say something.
40
45
 
41
46
  ```sh
42
47
  mc new shop-app --target-html --target-css
@@ -1 +1 @@
1
- {"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../../src/docs/installation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8BE;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA8CE"}
1
+ {"version":3,"file":"installation.d.ts","sourceRoot":"","sources":["../../src/docs/installation.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EA+BE;AAEF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAkDE"}
package/dist/mcp/api.json CHANGED
@@ -1,5 +1,5 @@
1
1
  {
2
- "libraryVersion": "0.13.0",
2
+ "libraryVersion": "0.13.2",
3
3
  "exportedCount": 59,
4
4
  "entries": {
5
5
  "MC": {
@@ -47,6 +47,14 @@ src/
47
47
 
48
48
  The DOM hierarchy is therefore immediately visible in the file system.
49
49
 
50
+ The name is the shortest one that says what the component does. The leaf segment is the
51
+ component's function, not its shape: `<shop-header-search>` → `src/shop/header/search.js`
52
+ says what the child of the header does. `<shop-header-panel>` → `src/shop/header/panel.js`
53
+ is wrong — `panel` describes a shape, so the file carries no information about the
54
+ component, and the folder level the tag creates is paid for by a word that says nothing.
55
+ Generic structural words (`panel`, `box`, `wrapper`, `widget`, `view`) are superfluous:
56
+ the nesting a tag encodes costs a directory, so the segment must be worth it.
57
+
50
58
  ---
51
59
 
52
60
  ## General-Purpose Components in Side Folders
@@ -111,6 +111,11 @@ External design of the composed unit is driven by namespaced CSS custom properti
111
111
  </cart-container>
112
112
  ```
113
113
 
114
+ A child's tag carries its parent's prefix, so its file lands in the parent's folder
115
+ (`<cart-container-articles>` → `src/cart/container/articles.ts`), and the last segment
116
+ names what the child does — `articles`, `checkout`, `pay`. A generic structural word
117
+ (`panel`, `box`, `wrapper`) makes the folder level it creates say nothing.
118
+
114
119
  ### 1d. Leaf Components
115
120
 
116
121
  Self-contained visual units that project no child custom elements — a button, a badge,
@@ -16,7 +16,7 @@ Every `.ts` or `.js` file contains exactly **one class**, regardless of which ty
16
16
  visual, logic-only, style-only, or pure logic. This applies to `DC` subclasses, `MC`
17
17
  subclasses, and any other class defined in `src/`.
18
18
 
19
- Why:
19
+ For the classes the toolchain registers, three mechanisms make the rule loadable:
20
20
 
21
21
  - **Autoload resolution** — `autoload()` maps a tag name to a single script path
22
22
  (`<shop-header>` → `./shop/header.js`). One class per file keeps that mapping
@@ -27,5 +27,25 @@ Why:
27
27
  - **Registry clarity** — `defineCE()` is called once per file, so file, class, and
28
28
  registered tag stay in a 1:1 relationship.
29
29
 
30
+ The three logic types are where the rule is easiest to lose, because none of those three
31
+ mechanisms can catch a violation:
32
+
33
+ - **Logic-only `DC`** (`static tag = ''`) — the class is registered, so the 1:1 contract
34
+ holds, and the asset name is the host's tag name, which for a removed host is the
35
+ registered tag (`app-router` → `./app/router.css`). The class runs setup logic on a
36
+ host it is attached to with `DC.add(host, key, instance)`: one instance under one key,
37
+ so a second class in the file is a second attachable unit with no key of its own. Its
38
+ companions are `.css` and `.json` — the constructor removes the host after the assets
39
+ are applied, so a markup companion has nothing to inject into.
40
+ - **Style-only `DC`** (`static tag = ''`, `static assets = ['css']`) — the class is a
41
+ delivery vehicle for one stylesheet. File, class, registered tag, and the `.css`
42
+ companion are one thing under four names: `page-cockpit` in `src/page/cockpit.ts`
43
+ fetches `./page/cockpit.css`. A second class in the file would ship a second
44
+ stylesheet under a basename the first one owns.
45
+ - **Pure logic `MC`** — no tag, no `defineCE`, no autoload path, so nothing in the
46
+ toolchain sees the file at all. The rule holds for the reason a reusable unit gets its
47
+ own file: the import is by path, so one file is one capability and the file name is
48
+ the capability. A helper that is not a class is a utility function, not an `MC`.
49
+
30
50
  Non-class entities that belong to the class — interfaces, types, constants, plain
31
51
  functions — may live alongside it. A class that is reusable on its own gets its own file.
@@ -4,9 +4,11 @@ A Micro Components project keeps one folder per stage of the chain:
4
4
  src/ → (tsc) → build/ → (bundle) → public/
5
5
  ```
6
6
 
7
- - `src/` holds the authored sources: component `.ts` files, the companion
8
- `.target.html` / `.target.css` / `.shadow.css` assets, the dev shell
9
- (`index-dev.html`) and the production shell (`index.html`).
7
+ - `src/` holds the authored sources: component `.ts` files, plain `.js` components
8
+ written with `mc new --js`, the companion `.target.html` / `.target.css` /
9
+ `.shadow.css` assets, the bundle barrel `src/<namespace>/bundle.ts` with the
10
+ `src/bundle.json` that names it, the dev shell (`index-dev.html`) and the production
11
+ shell (`index.html`).
10
12
  - `build/` is the `tsc` output: one `.js` per component at its own autoload path,
11
13
  plus the files `tsc` does not compile — mirrored companion assets, plain `.js`
12
14
  components, and the dev shell. `mc dev` serves it, so the page loads components
@@ -23,7 +25,8 @@ served from `public/` in production. Do not add a fourth tree for it.
23
25
 
24
26
  A new component is picked up by the dev server automatically, because `autoload()`
25
27
  resolves its tag to a file under `build/`. The bundle is not: the bundler walks a single
26
- entry, so every component must be imported by the barrel `src/<namespace>/bundle.ts`.
28
+ entry, the one `src/bundle.json` names, so every component must be imported by the barrel
29
+ `src/<namespace>/bundle.ts` that file points at.
27
30
 
28
31
  ## Shells
29
32
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@twobirds/microcomponents",
3
- "version": "0.13.0",
3
+ "version": "0.13.2",
4
4
  "description": "Micro Components",
5
5
  "keywords": [
6
6
  "PWA",