@octanejs/cli 0.0.9 → 0.0.10
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/package.json +1 -1
- package/src/commands/analyze.js +18 -6
- package/src/commands/init/templates.js +137 -116
- package/src/data/octane-data.json +451 -221
package/package.json
CHANGED
package/src/commands/analyze.js
CHANGED
|
@@ -11,7 +11,7 @@ import { SYMBOLS } from '../kernel/ui.js';
|
|
|
11
11
|
* @property {string} file relative to the project root
|
|
12
12
|
* @property {number} line
|
|
13
13
|
* @property {number} column 1-based, matching editor gutters
|
|
14
|
-
* @property {'error' | 'warning'} severity
|
|
14
|
+
* @property {'error' | 'warning' | 'hint'} severity
|
|
15
15
|
* @property {string} code
|
|
16
16
|
* @property {string} message
|
|
17
17
|
* @property {string[]} suggestions
|
|
@@ -175,7 +175,12 @@ export default defineCommand({
|
|
|
175
175
|
file,
|
|
176
176
|
line: diagnostic.start?.line ?? 1,
|
|
177
177
|
column: (diagnostic.start?.column ?? 0) + 1,
|
|
178
|
-
severity:
|
|
178
|
+
severity:
|
|
179
|
+
diagnostic.severity === 'error'
|
|
180
|
+
? 'error'
|
|
181
|
+
: diagnostic.severity === 'hint'
|
|
182
|
+
? 'hint'
|
|
183
|
+
: 'warning',
|
|
179
184
|
code: diagnostic.code,
|
|
180
185
|
// The compiler prefixes its own code; the report already has a
|
|
181
186
|
// column for it.
|
|
@@ -198,7 +203,8 @@ export default defineCommand({
|
|
|
198
203
|
render(ctx, selected, targets.length);
|
|
199
204
|
|
|
200
205
|
const errors = selected.filter((finding) => finding.severity === 'error').length;
|
|
201
|
-
const warnings = selected.
|
|
206
|
+
const warnings = selected.filter((finding) => finding.severity === 'warning').length;
|
|
207
|
+
const hints = selected.filter((finding) => finding.severity === 'hint').length;
|
|
202
208
|
const failed = errors > 0 || (input.flags.strict && warnings > 0);
|
|
203
209
|
|
|
204
210
|
return {
|
|
@@ -206,7 +212,7 @@ export default defineCommand({
|
|
|
206
212
|
json: {
|
|
207
213
|
ok: !failed,
|
|
208
214
|
analyzed: targets.length,
|
|
209
|
-
summary: { errors, warnings },
|
|
215
|
+
summary: { errors, warnings, hints },
|
|
210
216
|
findings: selected,
|
|
211
217
|
},
|
|
212
218
|
};
|
|
@@ -249,7 +255,11 @@ function render(ctx, findings, analyzed) {
|
|
|
249
255
|
|
|
250
256
|
for (const finding of findings.filter((entry) => entry.file === file)) {
|
|
251
257
|
const mark =
|
|
252
|
-
finding.severity === 'error'
|
|
258
|
+
finding.severity === 'error'
|
|
259
|
+
? colors.red(SYMBOLS.fail)
|
|
260
|
+
: finding.severity === 'hint'
|
|
261
|
+
? colors.dim('hint')
|
|
262
|
+
: colors.yellow(SYMBOLS.warn);
|
|
253
263
|
const where = colors.dim(`${finding.line}:${finding.column}`);
|
|
254
264
|
ctx.ui.log(` ${mark} ${where} ${finding.message}`);
|
|
255
265
|
ctx.ui.log(` ${colors.dim(finding.code)}`);
|
|
@@ -260,11 +270,13 @@ function render(ctx, findings, analyzed) {
|
|
|
260
270
|
}
|
|
261
271
|
|
|
262
272
|
const errors = findings.filter((finding) => finding.severity === 'error').length;
|
|
263
|
-
const warnings = findings.
|
|
273
|
+
const warnings = findings.filter((finding) => finding.severity === 'warning').length;
|
|
274
|
+
const hints = findings.filter((finding) => finding.severity === 'hint').length;
|
|
264
275
|
/** @type {string[]} */
|
|
265
276
|
const parts = [];
|
|
266
277
|
if (errors > 0) parts.push(colors.red(`${errors} error${errors === 1 ? '' : 's'}`));
|
|
267
278
|
if (warnings > 0) parts.push(colors.yellow(`${warnings} warning${warnings === 1 ? '' : 's'}`));
|
|
279
|
+
if (hints > 0) parts.push(colors.dim(`${hints} hint${hints === 1 ? '' : 's'}`));
|
|
268
280
|
|
|
269
281
|
ctx.ui.log('');
|
|
270
282
|
ctx.ui.log(`${parts.join(colors.dim(' · '))} ${colors.dim(`across ${analyzed} file(s)`)}`);
|
|
@@ -126,48 +126,64 @@ const LOGO = `<svg class="mark" viewBox="0 0 84 108" fill="none" aria-hidden="tr
|
|
|
126
126
|
* The documentation cards, matching the card treatment on octanejs.dev: the
|
|
127
127
|
* same radius, the same subtle surface, and the same accent-tinted hover rather
|
|
128
128
|
* than a plain border swap.
|
|
129
|
+
*
|
|
130
|
+
* An assigned `<style>` is a theme rather than a block scoped to one template:
|
|
131
|
+
* it keeps every selector and evaluates to a class map. The page applies it by
|
|
132
|
+
* putting `apply={linkTheme}` on its own `<style>` block, which stamps the
|
|
133
|
+
* theme's class on the items beside that block and everything below them —
|
|
134
|
+
* never on the element that contains it, which is why the block and the
|
|
135
|
+
* page's root are fragment siblings — so these rules reach them the same way
|
|
136
|
+
* the block's own rules do. One block rather than a self-closed
|
|
137
|
+
* `<style apply />` beside it: the type-checker's virtual TSX accepts only one
|
|
138
|
+
* `<style>` per element. The theme sits at module scope, above the component
|
|
139
|
+
* that applies it, which is the order the compiler requires.
|
|
129
140
|
*/
|
|
130
|
-
const
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
.
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
141
|
+
const LINK_THEME = `// The documentation cards. An assigned <style> is a theme: the page's own
|
|
142
|
+
// <style apply={linkTheme}> block below applies it, so its rules reach the
|
|
143
|
+
// items beside that block and everything below them.
|
|
144
|
+
const linkTheme = <style>
|
|
145
|
+
.links {
|
|
146
|
+
display: grid;
|
|
147
|
+
grid-template-columns: repeat(auto-fit, minmax(15rem, 1fr));
|
|
148
|
+
gap: 0.75rem;
|
|
149
|
+
width: 100%;
|
|
150
|
+
max-width: 42rem;
|
|
151
|
+
margin: 0;
|
|
152
|
+
padding: 0;
|
|
153
|
+
list-style: none;
|
|
154
|
+
}
|
|
155
|
+
.link {
|
|
156
|
+
display: block;
|
|
157
|
+
padding: 1rem;
|
|
158
|
+
border: 1px solid var(--border);
|
|
159
|
+
border-radius: 0.9rem;
|
|
160
|
+
background: var(--surface-subtle);
|
|
161
|
+
color: var(--text);
|
|
162
|
+
text-align: left;
|
|
163
|
+
text-decoration: none;
|
|
164
|
+
transition:
|
|
165
|
+
background 0.15s,
|
|
166
|
+
border-color 0.15s,
|
|
167
|
+
transform 0.15s;
|
|
168
|
+
}
|
|
169
|
+
.link:hover {
|
|
170
|
+
border-color: var(--card-hover-border);
|
|
171
|
+
background: var(--card-hover-bg);
|
|
172
|
+
transform: translateY(-1px);
|
|
173
|
+
}
|
|
174
|
+
.link-title {
|
|
175
|
+
display: block;
|
|
176
|
+
font-size: 0.95rem;
|
|
177
|
+
font-weight: 600;
|
|
178
|
+
}
|
|
179
|
+
.link-body {
|
|
180
|
+
display: block;
|
|
181
|
+
margin-top: 0.15rem;
|
|
182
|
+
color: var(--text-secondary);
|
|
183
|
+
font-size: 0.82rem;
|
|
184
|
+
line-height: 1.4;
|
|
185
|
+
}
|
|
186
|
+
</style>;`;
|
|
171
187
|
|
|
172
188
|
/** Where the landing page sends people next. */
|
|
173
189
|
const DOC_LINKS = `const LINKS = [
|
|
@@ -208,6 +224,8 @@ export const appComponent = (mode) =>
|
|
|
208
224
|
|
|
209
225
|
${DOC_LINKS}
|
|
210
226
|
|
|
227
|
+
${LINK_THEME}
|
|
228
|
+
|
|
211
229
|
export function App() @{
|
|
212
230
|
<Layout>
|
|
213
231
|
<section class="hero">
|
|
@@ -229,7 +247,7 @@ export function App() @{
|
|
|
229
247
|
</ul>
|
|
230
248
|
</section>
|
|
231
249
|
|
|
232
|
-
<style>
|
|
250
|
+
<style apply={linkTheme}>
|
|
233
251
|
.hero {
|
|
234
252
|
display: flex;
|
|
235
253
|
flex-direction: column;
|
|
@@ -253,33 +271,17 @@ export function App() @{
|
|
|
253
271
|
color: var(--text-secondary);
|
|
254
272
|
font-size: 1.25rem;
|
|
255
273
|
}
|
|
256
|
-
${LINK_STYLES}
|
|
257
274
|
</style>
|
|
258
275
|
</Layout>
|
|
259
276
|
}
|
|
260
277
|
`
|
|
261
278
|
: `${DOC_LINKS}
|
|
262
279
|
|
|
263
|
-
|
|
264
|
-
<main class="page">
|
|
265
|
-
${LOGO}
|
|
266
|
-
<h1 class="title">octane</h1>
|
|
267
|
-
<p class="lede">
|
|
268
|
-
React’s programming model, compiled. Edit src/App.tsrx and save — the page
|
|
269
|
-
updates without a reload.
|
|
270
|
-
</p>
|
|
271
|
-
<ul class="links">
|
|
272
|
-
@for (const link of LINKS; key link.href) {
|
|
273
|
-
<li>
|
|
274
|
-
<a class="link" href={link.href} target="_blank" rel="noreferrer">
|
|
275
|
-
<span class="link-title">{link.title as string}</span>
|
|
276
|
-
<span class="link-body">{link.body as string}</span>
|
|
277
|
-
</a>
|
|
278
|
-
</li>
|
|
279
|
-
}
|
|
280
|
-
</ul>
|
|
280
|
+
${LINK_THEME}
|
|
281
281
|
|
|
282
|
-
|
|
282
|
+
export function App() @{
|
|
283
|
+
<>
|
|
284
|
+
<style apply={linkTheme}>
|
|
283
285
|
.page {
|
|
284
286
|
display: flex;
|
|
285
287
|
min-height: 100dvh;
|
|
@@ -306,9 +308,26 @@ export function App() @{
|
|
|
306
308
|
color: var(--text-secondary);
|
|
307
309
|
font-size: 1.25rem;
|
|
308
310
|
}
|
|
309
|
-
${LINK_STYLES}
|
|
310
311
|
</style>
|
|
311
|
-
|
|
312
|
+
<main class="page">
|
|
313
|
+
${LOGO}
|
|
314
|
+
<h1 class="title">octane</h1>
|
|
315
|
+
<p class="lede">
|
|
316
|
+
React’s programming model, compiled. Edit src/App.tsrx and save — the page
|
|
317
|
+
updates without a reload.
|
|
318
|
+
</p>
|
|
319
|
+
<ul class="links">
|
|
320
|
+
@for (const link of LINKS; key link.href) {
|
|
321
|
+
<li>
|
|
322
|
+
<a class="link" href={link.href} target="_blank" rel="noreferrer">
|
|
323
|
+
<span class="link-title">{link.title as string}</span>
|
|
324
|
+
<span class="link-body">{link.body as string}</span>
|
|
325
|
+
</a>
|
|
326
|
+
</li>
|
|
327
|
+
}
|
|
328
|
+
</ul>
|
|
329
|
+
</main>
|
|
330
|
+
</>
|
|
312
331
|
}
|
|
313
332
|
`;
|
|
314
333
|
|
|
@@ -323,14 +342,7 @@ interface LayoutProps {
|
|
|
323
342
|
}
|
|
324
343
|
|
|
325
344
|
export function Layout(props: LayoutProps) @{
|
|
326
|
-
|
|
327
|
-
<nav class="nav">
|
|
328
|
-
<a class="nav-link" href="/">Home</a>
|
|
329
|
-
<a class="nav-link" href="/counter">Counter</a>
|
|
330
|
-
<a class={["nav-link", "nav-endpoint"]} href="/api/health">/api/health</a>
|
|
331
|
-
</nav>
|
|
332
|
-
<main class="main">{props.children}</main>
|
|
333
|
-
|
|
345
|
+
<>
|
|
334
346
|
<style>
|
|
335
347
|
.shell {
|
|
336
348
|
display: flex;
|
|
@@ -373,7 +385,15 @@ export function Layout(props: LayoutProps) @{
|
|
|
373
385
|
padding: 3rem 1.5rem;
|
|
374
386
|
}
|
|
375
387
|
</style>
|
|
376
|
-
|
|
388
|
+
<div class="shell">
|
|
389
|
+
<nav class="nav">
|
|
390
|
+
<a class="nav-link" href="/">Home</a>
|
|
391
|
+
<a class="nav-link" href="/counter">Counter</a>
|
|
392
|
+
<a class={["nav-link", "nav-endpoint"]} href="/api/health">/api/health</a>
|
|
393
|
+
</nav>
|
|
394
|
+
<main class="main">{props.children}</main>
|
|
395
|
+
</div>
|
|
396
|
+
</>
|
|
377
397
|
}
|
|
378
398
|
`;
|
|
379
399
|
|
|
@@ -391,6 +411,42 @@ export function Counter() @{
|
|
|
391
411
|
const [count, setCount] = useState(0);
|
|
392
412
|
|
|
393
413
|
<Layout>
|
|
414
|
+
<style>
|
|
415
|
+
.counter {
|
|
416
|
+
display: flex;
|
|
417
|
+
flex-direction: column;
|
|
418
|
+
align-items: center;
|
|
419
|
+
gap: 1.25rem;
|
|
420
|
+
text-align: center;
|
|
421
|
+
}
|
|
422
|
+
.title {
|
|
423
|
+
margin: 0;
|
|
424
|
+
font-size: 2rem;
|
|
425
|
+
line-height: 1.1;
|
|
426
|
+
letter-spacing: -0.02em;
|
|
427
|
+
}
|
|
428
|
+
.lede {
|
|
429
|
+
max-width: 34rem;
|
|
430
|
+
margin: 0;
|
|
431
|
+
color: var(--text-secondary);
|
|
432
|
+
font-size: 1.25rem;
|
|
433
|
+
}
|
|
434
|
+
/* The site's primary action: an accent pill. */
|
|
435
|
+
.button {
|
|
436
|
+
padding: 0.7rem 1.4rem;
|
|
437
|
+
border: none;
|
|
438
|
+
border-radius: 9999px;
|
|
439
|
+
background: var(--accent);
|
|
440
|
+
color: var(--on-accent);
|
|
441
|
+
font: inherit;
|
|
442
|
+
font-weight: 600;
|
|
443
|
+
cursor: pointer;
|
|
444
|
+
transition: background 0.15s;
|
|
445
|
+
}
|
|
446
|
+
.button:hover {
|
|
447
|
+
background: var(--accent-hover);
|
|
448
|
+
}
|
|
449
|
+
</style>
|
|
394
450
|
<section class="counter">
|
|
395
451
|
<h1 class="title">Server-rendered, then interactive</h1>
|
|
396
452
|
<p class="lede">
|
|
@@ -400,43 +456,6 @@ export function Counter() @{
|
|
|
400
456
|
<button class="button" onClick={() => setCount(count + 1)}>
|
|
401
457
|
{"Pressed " + count + " times"}
|
|
402
458
|
</button>
|
|
403
|
-
|
|
404
|
-
<style>
|
|
405
|
-
.counter {
|
|
406
|
-
display: flex;
|
|
407
|
-
flex-direction: column;
|
|
408
|
-
align-items: center;
|
|
409
|
-
gap: 1.25rem;
|
|
410
|
-
text-align: center;
|
|
411
|
-
}
|
|
412
|
-
.title {
|
|
413
|
-
margin: 0;
|
|
414
|
-
font-size: 2rem;
|
|
415
|
-
line-height: 1.1;
|
|
416
|
-
letter-spacing: -0.02em;
|
|
417
|
-
}
|
|
418
|
-
.lede {
|
|
419
|
-
max-width: 34rem;
|
|
420
|
-
margin: 0;
|
|
421
|
-
color: var(--text-secondary);
|
|
422
|
-
font-size: 1.25rem;
|
|
423
|
-
}
|
|
424
|
-
/* The site's primary action: an accent pill. */
|
|
425
|
-
.button {
|
|
426
|
-
padding: 0.7rem 1.4rem;
|
|
427
|
-
border: none;
|
|
428
|
-
border-radius: 9999px;
|
|
429
|
-
background: var(--accent);
|
|
430
|
-
color: var(--on-accent);
|
|
431
|
-
font: inherit;
|
|
432
|
-
font-weight: 600;
|
|
433
|
-
cursor: pointer;
|
|
434
|
-
transition: background 0.15s;
|
|
435
|
-
}
|
|
436
|
-
.button:hover {
|
|
437
|
-
background: var(--accent-hover);
|
|
438
|
-
}
|
|
439
|
-
</style>
|
|
440
459
|
</section>
|
|
441
460
|
</Layout>
|
|
442
461
|
}
|
|
@@ -462,12 +481,14 @@ export function health(): Response {
|
|
|
462
481
|
* The reset and the theme tokens every component reads, taken from
|
|
463
482
|
* octanejs.dev so a scaffolded app and the documentation look like one thing.
|
|
464
483
|
*
|
|
465
|
-
* A stylesheet rather than a `<style>` in the shell. A
|
|
466
|
-
*
|
|
467
|
-
*
|
|
468
|
-
*
|
|
469
|
-
*
|
|
470
|
-
*
|
|
484
|
+
* A stylesheet rather than a `<style>` in the shell. A `<style>` in a component
|
|
485
|
+
* styles only the items beside it and everything below them — the compiler
|
|
486
|
+
* rewrites its selectors with a scope hash — and a theme such as `linkTheme`
|
|
487
|
+
* reaches only the scopes that apply it, so neither can style `body`, and these custom
|
|
488
|
+
* properties have to resolve for every component at once. Keeping them in a
|
|
489
|
+
* file the shell links, rather than inline in the shell, means `init` still has
|
|
490
|
+
* something to point at when the project brought its own `index.html` and kept
|
|
491
|
+
* it.
|
|
471
492
|
*
|
|
472
493
|
* Linked from the shell rather than imported by a component on purpose: a CSS
|
|
473
494
|
* import is injected by JavaScript in dev, so the tokens would arrive after the
|