create-fluixi 0.1.0-alpha.10 → 0.1.0-alpha.12
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/dist/index.js +114 -17
- package/package.json +1 -1
- package/template-spa/src/app.tsx +3 -4
- package/template-spa-html/src/app.ts +4 -4
- package/template-ssr/src/app.tsx +3 -3
- package/template-ssr/src/routes/index.tsx +1 -2
- package/template-ssr-html/src/app.ts +2 -2
- package/template-ssr-html/src/routes/index.ts +1 -2
package/dist/index.js
CHANGED
|
@@ -140,25 +140,92 @@ const UI_DEPS = {
|
|
|
140
140
|
};
|
|
141
141
|
/** Build-time only: it runs in the config, not in the app. */
|
|
142
142
|
const UI_DEV_DEPS = { '@fluixi-ui/resolver': 'alpha' };
|
|
143
|
+
/**
|
|
144
|
+
* What `fluixi.d.ts` holds until the compiler regenerates it.
|
|
145
|
+
*
|
|
146
|
+
* Only the intrinsics: the component globals depend on which packages are installed and
|
|
147
|
+
* on the resolver rules, so listing them here would go stale the moment a component is
|
|
148
|
+
* added. The compiler rewrites the whole file on the first dev run.
|
|
149
|
+
*/
|
|
150
|
+
const INTRINSICS_DTS = `// GENERATED by @fluixi/compiler, do not edit.
|
|
151
|
+
//
|
|
152
|
+
// The reactive intrinsics, which the compiler lowers to their \`create*\` calls. They
|
|
153
|
+
// need no import. This file is rewritten on the first \`fluixi dev\`, which adds the
|
|
154
|
+
// components the resolver supplies without an import.
|
|
155
|
+
import * as __fluixi_reactive_signal from '@fluixi/reactive/signal';
|
|
156
|
+
import * as __fluixi_reactive_store from '@fluixi/reactive/store';
|
|
157
|
+
|
|
158
|
+
declare global {
|
|
159
|
+
const $signal: typeof __fluixi_reactive_signal.signal;
|
|
160
|
+
const $memo: typeof __fluixi_reactive_signal.memo;
|
|
161
|
+
const $effect: typeof __fluixi_reactive_signal.effect;
|
|
162
|
+
const $store: typeof __fluixi_reactive_store.store;
|
|
163
|
+
const $resource: typeof __fluixi_reactive_signal.resource;
|
|
164
|
+
const $selector: typeof __fluixi_reactive_signal.createSelector;
|
|
165
|
+
const $deferred: typeof __fluixi_reactive_signal.createDeferred;
|
|
166
|
+
const $untrack: typeof __fluixi_reactive_signal.untrack;
|
|
167
|
+
const $untrackStore: typeof __fluixi_reactive_store.untrackStore;
|
|
168
|
+
}
|
|
169
|
+
`;
|
|
170
|
+
/**
|
|
171
|
+
* The cascade-layer order, declared before anything defines a layer.
|
|
172
|
+
*
|
|
173
|
+
* A layer ranks by where it is FIRST declared, and one nobody declared is appended last,
|
|
174
|
+
* which makes it the strongest. That default is wrong twice over here. The fluixi-css
|
|
175
|
+
* reset defines `flx.base`, which @fluixi-ui's own sheets do not declare, so the reset
|
|
176
|
+
* would outrank the components it is meant to sit under, and an app's plain
|
|
177
|
+
* `button { ... }` reset would outrank them too.
|
|
178
|
+
*
|
|
179
|
+
* Declaring the order up front fixes both: the app's element resets sit lowest, then the
|
|
180
|
+
* engine's preflight, then the component layers. An app's own `@layer base` and
|
|
181
|
+
* `@layer components` are deliberately absent, so they stay unregistered and keep
|
|
182
|
+
* winning, which is what an app expects of its own classes.
|
|
183
|
+
*/
|
|
184
|
+
const LAYERS_CSS = `/* Cascade-layer order. Imported before any component CSS, because a layer ranks by
|
|
185
|
+
where it is first declared and an undeclared one would rank last, above everything.
|
|
186
|
+
Add this app's own layers only if you want them to rank BELOW the component styles. */
|
|
187
|
+
@layer app-reset, flx.reset, flx.base, flx.tokens, flx.components, flx.skin;
|
|
188
|
+
`;
|
|
143
189
|
/** Add to a package.json section without dropping what the template already had. */
|
|
144
190
|
function addDeps(pkg, section, deps) {
|
|
145
191
|
pkg[section] = Object.fromEntries(Object.entries({ ...(pkg[section] ?? {}), ...deps }).sort(([a], [b]) => a.localeCompare(b)));
|
|
146
192
|
}
|
|
147
193
|
/**
|
|
148
|
-
* The stylesheet the
|
|
194
|
+
* The app's own stylesheet, in the shape the chosen css engine expects.
|
|
149
195
|
*
|
|
150
|
-
*
|
|
151
|
-
*
|
|
196
|
+
* The component tokens are not here: they are a stylesheet the package ships and the
|
|
197
|
+
* entry imports directly, so this file only holds what the engine has to read, the dark
|
|
198
|
+
* variant, where to scan, and the theme the app defines.
|
|
152
199
|
*/
|
|
153
200
|
function styleSheet(ui, css) {
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
201
|
+
// Dark from the attribute @fluixi/core's createTheme sets, not the OS preference, so
|
|
202
|
+
// a theme toggle in the app actually drives it.
|
|
203
|
+
const darkVariant = `@custom-variant dark (&:where([data-theme='dark'], [data-theme='dark'] *));`;
|
|
204
|
+
// Where to look for utility classes. Relative to this file.
|
|
205
|
+
const sources = `@source '.';\n@source '../index.html';`;
|
|
206
|
+
const theme = `@theme {\n /* Design tokens. A --color-* scale here drives the whole app. */\n}`;
|
|
207
|
+
if (css === 'fluixi-css') {
|
|
208
|
+
// The layers are pulled in by directive rather than by import, and they come last:
|
|
209
|
+
// `@theme` above has to be read before `utilities` is generated from it.
|
|
210
|
+
return [
|
|
211
|
+
darkVariant,
|
|
212
|
+
'',
|
|
213
|
+
sources,
|
|
214
|
+
'',
|
|
215
|
+
theme,
|
|
216
|
+
'',
|
|
217
|
+
'@fluixi base;',
|
|
218
|
+
'@fluixi utilities;',
|
|
219
|
+
'@fluixi components;',
|
|
220
|
+
'',
|
|
221
|
+
].join('\n');
|
|
222
|
+
}
|
|
223
|
+
if (css === 'tailwind') {
|
|
224
|
+
return [`@import 'tailwindcss';`, '', darkVariant, '', theme, ''].join('\n');
|
|
225
|
+
}
|
|
226
|
+
// No css engine: the component tokens are imported from the entry like any other
|
|
227
|
+
// stylesheet, so there is nothing for this file to hold.
|
|
228
|
+
return '';
|
|
162
229
|
}
|
|
163
230
|
/** Where the component resolver is configured, which differs by mode. */
|
|
164
231
|
const configFor = (mode) => mode === 'ssr' ? 'fluixi.config.ts' : 'vite.config.ts';
|
|
@@ -199,15 +266,29 @@ function addVitePlugin(target, mode, importLine, call) {
|
|
|
199
266
|
: src.replace('plugins: [', `plugins: [\n ${call},`);
|
|
200
267
|
writeFileSync(file, src);
|
|
201
268
|
}
|
|
202
|
-
/**
|
|
203
|
-
|
|
269
|
+
/**
|
|
270
|
+
* Import the stylesheets from whichever file boots the client.
|
|
271
|
+
*
|
|
272
|
+
* The component tokens come first and from the entry rather than from `styles.css`:
|
|
273
|
+
* they are a stylesheet the package ships, not something the app's css engine should be
|
|
274
|
+
* asked to resolve, and the components below need them defined before their own rules
|
|
275
|
+
* land. `emitUiTokens` does not replace this, that bridge covers a subset.
|
|
276
|
+
*/
|
|
277
|
+
function importStyles(target, mode, ui, css) {
|
|
204
278
|
const base = mode === 'ssr' ? 'entry-client' : 'main';
|
|
205
279
|
const file = ['tsx', 'ts']
|
|
206
280
|
.map((ext) => join(target, 'src', `${base}.${ext}`))
|
|
207
281
|
.find((p) => existsSync(p));
|
|
208
282
|
if (!file)
|
|
209
283
|
return;
|
|
210
|
-
|
|
284
|
+
const head = [
|
|
285
|
+
// First, before anything that defines a layer.
|
|
286
|
+
...(css === 'fluixi-css' ? [`import './layers.css';`] : []),
|
|
287
|
+
...(ui === 'fluixi-ui' ? [`import '@fluixi-ui/tokens/tokens.css';`] : []),
|
|
288
|
+
`import './styles.css';`,
|
|
289
|
+
'',
|
|
290
|
+
].join('\n');
|
|
291
|
+
writeFileSync(file, head + readFileSync(file, 'utf8'));
|
|
211
292
|
}
|
|
212
293
|
async function main() {
|
|
213
294
|
const args = argv.slice(2);
|
|
@@ -267,6 +348,13 @@ async function main() {
|
|
|
267
348
|
const gi = join(target, '_gitignore');
|
|
268
349
|
if (existsSync(gi))
|
|
269
350
|
renameSync(gi, join(target, '.gitignore'));
|
|
351
|
+
// The ambient declarations for the `$` intrinsics.
|
|
352
|
+
//
|
|
353
|
+
// The compiler rewrites this file on the first `fluixi dev`, adding whatever the
|
|
354
|
+
// resolver supplies without an import. Shipping it now means an editor knows what
|
|
355
|
+
// `$signal` is before the app has ever been run, instead of reporting "Cannot find
|
|
356
|
+
// name" across a project that compiles.
|
|
357
|
+
writeFileSync(join(target, 'fluixi.d.ts'), INTRINSICS_DTS);
|
|
270
358
|
const pkgPath = join(target, 'package.json');
|
|
271
359
|
const pkg = JSON.parse(readFileSync(pkgPath, 'utf8'));
|
|
272
360
|
pkg.name = name;
|
|
@@ -286,11 +374,20 @@ async function main() {
|
|
|
286
374
|
// The bridge only matters with the component kit installed: it maps the theme
|
|
287
375
|
// scales onto the `--flx-ui-*` custom properties those components read.
|
|
288
376
|
`fluixiCss({ reset: true${ui === 'fluixi-ui' ? ', emitUiTokens: true' : ''} })`);
|
|
289
|
-
|
|
377
|
+
// No `fluixi.css.config.js`: `@theme`, `@source` and `@custom-variant` in the
|
|
378
|
+
// stylesheet say the same things, and a project with both has two places to change
|
|
379
|
+
// one colour. The config file is still read when an app adds one.
|
|
290
380
|
}
|
|
291
381
|
if (ui === 'fluixi-ui' || css !== 'none') {
|
|
292
|
-
|
|
293
|
-
|
|
382
|
+
// Only for fluixi-css: it is the engine that defines `flx.base`, and without a
|
|
383
|
+
// declared order that layer lands above the components it belongs under.
|
|
384
|
+
if (css === 'fluixi-css')
|
|
385
|
+
writeFileSync(join(target, 'src', 'layers.css'), LAYERS_CSS);
|
|
386
|
+
const sheet = styleSheet(ui, css);
|
|
387
|
+
// An empty sheet still gets written: it is where an app puts its own css, and the
|
|
388
|
+
// entry already imports it.
|
|
389
|
+
writeFileSync(join(target, 'src', 'styles.css'), sheet);
|
|
390
|
+
importStyles(target, mode, ui, css);
|
|
294
391
|
}
|
|
295
392
|
writeFileSync(pkgPath, JSON.stringify(pkg, null, 2) + '\n');
|
|
296
393
|
// Outro + next steps. The leading gutter only makes sense after the intro/prompts.
|
package/package.json
CHANGED
package/template-spa/src/app.tsx
CHANGED
|
@@ -1,13 +1,12 @@
|
|
|
1
|
-
import { createSignal } from '@fluixi/core';
|
|
2
1
|
|
|
3
2
|
export function App() {
|
|
4
|
-
const
|
|
3
|
+
const count = $signal(0);
|
|
5
4
|
return (
|
|
6
5
|
<div class="app">
|
|
7
6
|
<h1>Fluixi</h1>
|
|
8
|
-
<button onClick={() =>
|
|
7
|
+
<button onClick={() => count.set(count() - 1)}>-</button>
|
|
9
8
|
<span>{count()}</span>
|
|
10
|
-
<button onClick={() =>
|
|
9
|
+
<button onClick={() => count.set(count() + 1)}>+</button>
|
|
11
10
|
</div>
|
|
12
11
|
);
|
|
13
12
|
}
|
|
@@ -1,13 +1,13 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { html } from '@fluixi/core';
|
|
2
2
|
|
|
3
3
|
export function App() {
|
|
4
|
-
const
|
|
4
|
+
const count = $signal(0);
|
|
5
5
|
return html`
|
|
6
6
|
<div class="app">
|
|
7
7
|
<h1>Fluixi</h1>
|
|
8
|
-
<button @click=${() =>
|
|
8
|
+
<button @click=${() => count.set(count() - 1)}>-</button>
|
|
9
9
|
<span>${count()}</span>
|
|
10
|
-
<button @click=${() =>
|
|
10
|
+
<button @click=${() => count.set(count() + 1)}>+</button>
|
|
11
11
|
</div>
|
|
12
12
|
`;
|
|
13
13
|
}
|
package/template-ssr/src/app.tsx
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Router, Outlet, createMemoryHistory } from '@fluixi/start/router';
|
|
1
|
+
import { Router, Outlet, Link, createMemoryHistory } from '@fluixi/start/router';
|
|
2
2
|
import { Suspense } from '@fluixi/core';
|
|
3
3
|
import { routes } from '@fluixi/core/routes';
|
|
4
4
|
|
|
@@ -8,9 +8,9 @@ function RootLayout() {
|
|
|
8
8
|
return (
|
|
9
9
|
<div class="app">
|
|
10
10
|
<nav>
|
|
11
|
-
<
|
|
11
|
+
<Link href="/">home</Link>
|
|
12
12
|
{' · '}
|
|
13
|
-
<
|
|
13
|
+
<Link href="/about">about</Link>
|
|
14
14
|
</nav>
|
|
15
15
|
<main>
|
|
16
16
|
<Suspense fallback={<p>loading…</p>}>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createResource } from '@fluixi/reactive/signal';
|
|
2
1
|
import { seo } from '@fluixi/start/head';
|
|
3
2
|
|
|
4
3
|
const tick = <T,>(v: T, ms = 30) => new Promise<T>((r) => setTimeout(() => r(v), ms));
|
|
@@ -6,7 +5,7 @@ const tick = <T,>(v: T, ms = 30) => new Promise<T>((r) => setTimeout(() => r(v),
|
|
|
6
5
|
// A data-gated route, like a real page: the resource is awaited during SSR so the
|
|
7
6
|
// content (not the spinner) is server-rendered, then hydrated.
|
|
8
7
|
export default function Home() {
|
|
9
|
-
const
|
|
8
|
+
const data = $resource(() => tick('Hello from Fluixi Start 👋'));
|
|
10
9
|
|
|
11
10
|
// Per-route document head: rendered into <head> on the server (great for crawlers/links)
|
|
12
11
|
// and kept reactive on the client. The head engine is always on; seo() is optional, call it
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Router, Outlet, createMemoryHistory } from '@fluixi/start/router';
|
|
1
|
+
import { Router, Outlet, createMemoryHistory, Link } from '@fluixi/start/router';
|
|
2
2
|
import { Suspense } from '@fluixi/core';
|
|
3
3
|
import { html } from '@fluixi/start';
|
|
4
4
|
import { routes } from '@fluixi/core/routes';
|
|
@@ -10,7 +10,7 @@ function RootLayout() {
|
|
|
10
10
|
return html`
|
|
11
11
|
<div class="app">
|
|
12
12
|
<nav>
|
|
13
|
-
|
|
13
|
+
<${Link} href="/">home<//> · <${Link} href="/about">about<//>
|
|
14
14
|
</nav>
|
|
15
15
|
<main>
|
|
16
16
|
<${Suspense} fallback=${html`<p>loading…</p>`}>
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { createResource } from '@fluixi/reactive/signal';
|
|
2
1
|
import { seo } from '@fluixi/start/head';
|
|
3
2
|
import { html } from '@fluixi/start';
|
|
4
3
|
|
|
@@ -7,7 +6,7 @@ const tick = <T,>(v: T, ms = 30) => new Promise<T>((r) => setTimeout(() => r(v),
|
|
|
7
6
|
// A data-gated route, like a real page: the resource is awaited during SSR so the
|
|
8
7
|
// content (not the spinner) is server-rendered, then hydrated.
|
|
9
8
|
export default function Home() {
|
|
10
|
-
const
|
|
9
|
+
const data = $resource(() => tick('Hello from Fluixi Start 👋'));
|
|
11
10
|
|
|
12
11
|
// Per-route document head: rendered into <head> on the server (great for crawlers/links)
|
|
13
12
|
// and kept reactive on the client. seo() is optional; call it where you want metadata.
|