@twobirds/microcomponents 0.8.0 → 0.10.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -16
- package/dist/cli/bundle.d.ts +106 -0
- package/dist/cli/bundle.d.ts.map +1 -0
- package/dist/cli/bundler.d.ts +44 -0
- package/dist/cli/bundler.d.ts.map +1 -0
- package/dist/cli/dev.d.ts +87 -0
- package/dist/cli/dev.d.ts.map +1 -0
- package/dist/cli/dispatch.d.ts +6 -2
- package/dist/cli/dispatch.d.ts.map +1 -1
- package/dist/cli/index.js +1807 -1
- package/dist/cli/inliner.d.ts +42 -0
- package/dist/cli/inliner.d.ts.map +1 -0
- package/dist/cli/preview.d.ts +48 -0
- package/dist/cli/preview.d.ts.map +1 -0
- package/dist/core/elements.js +1294 -75
- package/dist/core/mcGuards.js +5 -8
- package/dist/mcp/api.json +2 -17
- package/dist/mcp/concepts/project-layout.md +9 -8
- package/dist/mcp/index.js +450 -230
- package/dist/microcomponents.js +2882 -9
- package/dist/state/formValues.js +2236 -281
- package/dist/state/observables.js +1794 -342
- package/dist/utils/events.js +171 -197
- package/package.json +3 -7
- package/dist/cli/create.js +0 -568
- package/dist/cli/dispatch.js +0 -316
- package/dist/cli/scaffold.js +0 -109
- package/dist/cli/writeScaffold.js +0 -114
- package/dist/core/MC.js +0 -837
- package/dist/core/index.js +0 -72
- package/dist/state/index.js +0 -5
- package/dist/state/placeholders.js +0 -187
- package/dist/types/index.js +0 -1
- package/dist/types/mcTypes.js +0 -9
- package/dist/utils/autoload.js +0 -160
- package/dist/utils/helpers.js +0 -532
- package/dist/utils/index.js +0 -12
- package/dist/utils/resolveComponentPath.js +0 -39
- package/dist/vite/index.d.ts +0 -7
- package/dist/vite/index.d.ts.map +0 -1
- package/dist/vite/index.js +0 -6
- package/dist/vite/loadAssets.d.ts +0 -36
- package/dist/vite/loadAssets.d.ts.map +0 -1
- package/dist/vite/loadAssets.js +0 -221
package/dist/vite/loadAssets.js
DELETED
|
@@ -1,221 +0,0 @@
|
|
|
1
|
-
// oxlint-disable no-await-in-loop
|
|
2
|
-
/**
|
|
3
|
-
Inlines `static assets = [...]` declarations on DC classes for a bundled build: companion
|
|
4
|
-
`.css` / `.html` files are read at build time and embedded as a `static _assets = { ... }`
|
|
5
|
-
property inserted as the first line inside the class body, so `loadAssets()` never fetches
|
|
6
|
-
them at runtime. The original `static assets` line is preserved for debugging purposes.
|
|
7
|
-
|
|
8
|
-
The plugin ships as its own entry, `@twobirds/microcomponents/vite`, so a project that
|
|
9
|
-
bundles to `public/` imports it without copying the source.
|
|
10
|
-
|
|
11
|
-
@example
|
|
12
|
-
```ts
|
|
13
|
-
import { loadAssetsPlugin } from '@twobirds/microcomponents/vite';
|
|
14
|
-
|
|
15
|
-
export default defineConfig({
|
|
16
|
-
plugins: [loadAssetsPlugin(sourceRoot)],
|
|
17
|
-
});
|
|
18
|
-
```
|
|
19
|
-
|
|
20
|
-
@module Vite
|
|
21
|
-
*/
|
|
22
|
-
import { readFile } from 'node:fs/promises';
|
|
23
|
-
import path from 'node:path';
|
|
24
|
-
/**
|
|
25
|
-
* Escape a raw string so it can be safely embedded inside a JS template literal.
|
|
26
|
-
* Backticks and `${` sequences are escaped to prevent breaking the enclosing literal.
|
|
27
|
-
*
|
|
28
|
-
* @example Returns the input with backticks and `${` sequences escaped.
|
|
29
|
-
* ```ts
|
|
30
|
-
* escapeTemplateLiteral('price is ${cost}`done`');
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
function escapeTemplateLiteral(raw) {
|
|
34
|
-
return raw.replace(/\\/g, '\\\\').replace(/\$/g, '\\$').replace(/`/g, '\\`');
|
|
35
|
-
}
|
|
36
|
-
/**
|
|
37
|
-
* Resolve the relative file path for a given asset descriptor and component tag.
|
|
38
|
-
* Only the last segment of the tag is used as the basename since the .ts source
|
|
39
|
-
* already lives in the correct directory (e.g. `cart-container-checkout.ts` is
|
|
40
|
-
* inside `cart/container/`, so we just need `checkout.target.html`).
|
|
41
|
-
*
|
|
42
|
-
* @example Returns `./header.shadow.css`, `./card.target.html` and `./summary.css` for the calls below.
|
|
43
|
-
* ```ts
|
|
44
|
-
* resolveAssetPath('shop-header', 'shadowCss');
|
|
45
|
-
* resolveAssetPath('product-card', 'targetHtml');
|
|
46
|
-
* resolveAssetPath('cart-summary', 'css');
|
|
47
|
-
* ```
|
|
48
|
-
*/
|
|
49
|
-
function resolveAssetPath(tagName, asset) {
|
|
50
|
-
const basename = tagName.split('-').pop() ?? tagName;
|
|
51
|
-
const m = asset.match(/^([a-z]+)([A-Z][a-z]*)$/);
|
|
52
|
-
if (m) {
|
|
53
|
-
return `./${basename}.${m[1]}.${m[2].toLowerCase()}`;
|
|
54
|
-
}
|
|
55
|
-
return `./${basename}.${asset.toLowerCase()}`;
|
|
56
|
-
}
|
|
57
|
-
/**
|
|
58
|
-
* Extract the tag name from a `defineCE('tag-name', ...)` call in source code.
|
|
59
|
-
* Returns the first found tag or undefined.
|
|
60
|
-
*
|
|
61
|
-
* @example Returns `'shop-header'` for the code below.
|
|
62
|
-
* ```ts
|
|
63
|
-
* extractDefineCETag("defineCE('shop-header', ShopHeader);");
|
|
64
|
-
* ```
|
|
65
|
-
*/
|
|
66
|
-
function extractDefineCETag(code) {
|
|
67
|
-
const m = code.match(/defineCE\s*\(\s*['"]([^'"]+)['"]/);
|
|
68
|
-
return m ? m[1] : undefined;
|
|
69
|
-
}
|
|
70
|
-
/**
|
|
71
|
-
* Strip all line and block comments from source code, replacing
|
|
72
|
-
* their content with equivalent whitespace so character positions stay valid for
|
|
73
|
-
* later string slicing.
|
|
74
|
-
*
|
|
75
|
-
* @example Blanks out the `// note` segment while keeping every character
|
|
76
|
-
* position and line break, so later string slicing stays aligned.
|
|
77
|
-
* ```ts
|
|
78
|
-
* stripComments('const a = 1; // note\nconst b = 2;');
|
|
79
|
-
* ```
|
|
80
|
-
*/
|
|
81
|
-
function stripComments(code) {
|
|
82
|
-
let result = '';
|
|
83
|
-
let i = 0;
|
|
84
|
-
while (i < code.length) {
|
|
85
|
-
// Line comment
|
|
86
|
-
if (code[i] === '/' && code[i + 1] === '/') {
|
|
87
|
-
const end = code.indexOf('\n', i);
|
|
88
|
-
if (end === -1) {
|
|
89
|
-
result += ' '.repeat(code.length - i);
|
|
90
|
-
break;
|
|
91
|
-
}
|
|
92
|
-
result += ' '.repeat(end - i) + '\n';
|
|
93
|
-
i = end + 1;
|
|
94
|
-
}
|
|
95
|
-
else if (code[i] === '/' && code[i + 1] === '*') {
|
|
96
|
-
const end = code.indexOf('*/', i + 2);
|
|
97
|
-
if (end === -1) {
|
|
98
|
-
result += ' '.repeat(code.length - i);
|
|
99
|
-
break;
|
|
100
|
-
}
|
|
101
|
-
// Preserve newlines inside block comments to keep line positions valid
|
|
102
|
-
let j = i;
|
|
103
|
-
while (j <= end + 1) {
|
|
104
|
-
result += code[j] === '\n' ? '\n' : ' ';
|
|
105
|
-
j++;
|
|
106
|
-
}
|
|
107
|
-
i = end + 2;
|
|
108
|
-
}
|
|
109
|
-
else {
|
|
110
|
-
result += code[i];
|
|
111
|
-
i++;
|
|
112
|
-
}
|
|
113
|
-
}
|
|
114
|
-
return result;
|
|
115
|
-
}
|
|
116
|
-
/**
|
|
117
|
-
* Read and inline all assets for a given tag, returning an array of
|
|
118
|
-
* `{ key, literal }` entries suitable for building object literals.
|
|
119
|
-
* Plain `.css` files hold CSS custom-property presets and are inlined with
|
|
120
|
-
* their real content so the component injects the default variables at
|
|
121
|
-
* runtime; the same files are also merged into bundle.css as the override
|
|
122
|
-
* placeholder. They are never left empty — an empty string would make the
|
|
123
|
-
* runtime treat the asset as not-yet-loaded and re-fetch it.
|
|
124
|
-
*
|
|
125
|
-
* @example Returns one escaped template literal per descriptor, ready to be joined into an object.
|
|
126
|
-
* ```ts
|
|
127
|
-
* readAssetsInline('/src/shop/header', 'shop-header', ['shadowCss', 'targetHtml']);
|
|
128
|
-
* ```
|
|
129
|
-
*
|
|
130
|
-
* @throws If a companion file for a descriptor does not exist.
|
|
131
|
-
*/
|
|
132
|
-
async function readAssetsInline(dir, tagName, assets) {
|
|
133
|
-
const entries = [];
|
|
134
|
-
for (const asset of assets) {
|
|
135
|
-
const relPath = resolveAssetPath(tagName, asset);
|
|
136
|
-
const filePath = path.resolve(dir, relPath);
|
|
137
|
-
try {
|
|
138
|
-
const content = await readFile(filePath, 'utf8');
|
|
139
|
-
entries.push({
|
|
140
|
-
key: asset,
|
|
141
|
-
literal: '`' +
|
|
142
|
-
escapeTemplateLiteral(content)
|
|
143
|
-
.replace(/[\n\t\r]/g, ' ')
|
|
144
|
-
.replace(/\s\s/g, ' ') +
|
|
145
|
-
'`',
|
|
146
|
-
});
|
|
147
|
-
}
|
|
148
|
-
catch (_err) {
|
|
149
|
-
throw new Error(`[load-assets] Cannot resolve asset "${asset}" for component "${tagName}": file not found at ${filePath}`, { cause: _err });
|
|
150
|
-
}
|
|
151
|
-
}
|
|
152
|
-
return entries;
|
|
153
|
-
}
|
|
154
|
-
/**
|
|
155
|
-
* Vite plugin factory that inlines companion `.css` / `.html` assets into the
|
|
156
|
-
* DC classes declaring them through `static assets = [...]`.
|
|
157
|
-
*
|
|
158
|
-
* @example Register the plugin in a Vite config, pointing at the source root.
|
|
159
|
-
* ```ts
|
|
160
|
-
* import { loadAssetsPlugin } from './vite-plugin-load-assets';
|
|
161
|
-
* export default defineConfig({ plugins: [loadAssetsPlugin(__dirname)] });
|
|
162
|
-
* ```
|
|
163
|
-
*
|
|
164
|
-
* @param rootDir Directory holding the component source files.
|
|
165
|
-
* @returns A Vite plugin option.
|
|
166
|
-
*/
|
|
167
|
-
export function loadAssetsPlugin(rootDir) {
|
|
168
|
-
return {
|
|
169
|
-
name: 'load-assets-inline',
|
|
170
|
-
async transform(code, id) {
|
|
171
|
-
if (!code.includes('static assets'))
|
|
172
|
-
return null;
|
|
173
|
-
const dir = path.dirname(id);
|
|
174
|
-
// Strip comments so JSDoc examples don't trigger false matches.
|
|
175
|
-
// Whitespace-preserving replacement keeps character indices valid.
|
|
176
|
-
const stripped = stripComments(code);
|
|
177
|
-
// Resolve the component tag name from defineCE(...) call
|
|
178
|
-
const tagName = extractDefineCETag(stripped);
|
|
179
|
-
if (!tagName)
|
|
180
|
-
return null;
|
|
181
|
-
// Match: static assets = [ 'asset1' , 'asset2' ];
|
|
182
|
-
const saRegex = /static\s+assets\s*=\s*\[(.*?)\]\s*;?/gs;
|
|
183
|
-
const saMatches = [...stripped.matchAll(saRegex)];
|
|
184
|
-
if (saMatches.length === 0)
|
|
185
|
-
return null;
|
|
186
|
-
let transformed = code;
|
|
187
|
-
// There should be exactly one per file, but handle all in reverse
|
|
188
|
-
for (let i = saMatches.length - 1; i >= 0; i--) {
|
|
189
|
-
const match = saMatches[i];
|
|
190
|
-
const assetListRaw = match[1];
|
|
191
|
-
const assets = assetListRaw
|
|
192
|
-
.split(',')
|
|
193
|
-
.map((a) => a.trim().replace(/^['"]|['"]$/g, ''))
|
|
194
|
-
.filter(Boolean);
|
|
195
|
-
if (assets.length === 0)
|
|
196
|
-
continue;
|
|
197
|
-
// Read and inline all asset contents
|
|
198
|
-
const entries = await readAssetsInline(dir, tagName, assets);
|
|
199
|
-
// Build the static _assets property to inject as first line in class body
|
|
200
|
-
const objEntries = entries.map((e) => `${e.key}: ${e.literal}`);
|
|
201
|
-
const assetsObj = `{ ${objEntries.join(', ')} }`;
|
|
202
|
-
// Find the opening brace of the class that owns this `static assets` declaration.
|
|
203
|
-
// Search backwards from the match position to locate "class ... {" — we need the
|
|
204
|
-
// index of the opening brace so we can insert right after it.
|
|
205
|
-
const beforeMatch = stripped.substring(0, match.index);
|
|
206
|
-
const classOpenMatch = /class\s+\w+[^{]*\{/.exec(beforeMatch);
|
|
207
|
-
if (!classOpenMatch)
|
|
208
|
-
continue;
|
|
209
|
-
// Absolute position of the opening brace in `transformed` (= same as in `stripped`)
|
|
210
|
-
const classBraceIndex = classOpenMatch.index + classOpenMatch[0].length - 1;
|
|
211
|
-
// Build injection code — static _assets as first line inside the class body
|
|
212
|
-
const injectCode = `\n static _assets = ${assetsObj};\n`;
|
|
213
|
-
transformed =
|
|
214
|
-
transformed.substring(0, classBraceIndex + 1) +
|
|
215
|
-
injectCode +
|
|
216
|
-
transformed.substring(classBraceIndex + 1);
|
|
217
|
-
}
|
|
218
|
-
return transformed;
|
|
219
|
-
},
|
|
220
|
-
};
|
|
221
|
-
}
|