bestax-migrate 2.26.0 → 2.28.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 +1 -1
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +12 -6
- package/dist/sources/_shared/deps-common.d.ts +2 -0
- package/dist/sources/_shared/deps-common.d.ts.map +1 -1
- package/dist/sources/_shared/deps-common.js +3 -1
- package/dist/sources/_shared/polymorphic.d.ts.map +1 -1
- package/dist/sources/_shared/polymorphic.js +16 -1
- package/dist/sources/bloomer/mapping.d.ts.map +1 -1
- package/dist/sources/bloomer/mapping.js +9 -4
- package/dist/sources/bloomer/specials.d.ts.map +1 -1
- package/dist/sources/bloomer/specials.js +30 -3
- package/dist/sources/bulma-classes/class-map.d.ts +41 -10
- package/dist/sources/bulma-classes/class-map.d.ts.map +1 -1
- package/dist/sources/bulma-classes/class-map.js +69 -13
- package/dist/sources/bulma-classes/glyph.d.ts +20 -0
- package/dist/sources/bulma-classes/glyph.d.ts.map +1 -1
- package/dist/sources/bulma-classes/glyph.js +64 -0
- package/dist/sources/bulma-classes/html-assets.d.ts +87 -0
- package/dist/sources/bulma-classes/html-assets.d.ts.map +1 -0
- package/dist/sources/bulma-classes/html-assets.js +246 -0
- package/dist/sources/bulma-classes/html-rules.d.ts +5 -0
- package/dist/sources/bulma-classes/html-rules.d.ts.map +1 -1
- package/dist/sources/bulma-classes/html-rules.js +14 -2
- package/dist/sources/bulma-classes/html.d.ts +12 -4
- package/dist/sources/bulma-classes/html.d.ts.map +1 -1
- package/dist/sources/bulma-classes/html.js +224 -49
- package/dist/sources/bulma-classes/plan.d.ts +40 -1
- package/dist/sources/bulma-classes/plan.d.ts.map +1 -1
- package/dist/sources/bulma-classes/plan.js +160 -24
- package/dist/sources/bulma-classes/project.d.ts +10 -2
- package/dist/sources/bulma-classes/project.d.ts.map +1 -1
- package/dist/sources/bulma-classes/project.js +17 -8
- package/dist/sources/bulma-classes/transform.d.ts +2 -1
- package/dist/sources/bulma-classes/transform.d.ts.map +1 -1
- package/dist/sources/bulma-classes/transform.js +231 -33
- package/dist/sources/rbx/mapping.d.ts +10 -0
- package/dist/sources/rbx/mapping.d.ts.map +1 -1
- package/dist/sources/rbx/mapping.js +13 -7
- package/dist/sources/react-bulma-components/mapping.d.ts.map +1 -1
- package/dist/sources/react-bulma-components/mapping.js +19 -5
- package/package.json +4 -3
|
@@ -11,10 +11,16 @@
|
|
|
11
11
|
* What a component can't carry becomes a `TODO(bestax-migrate)` line and a
|
|
12
12
|
* report entry under the `html-*` rules (`html-rules.ts`): the head, the
|
|
13
13
|
* `<html>` and `<body>` attributes, scripts (dropped, since a component runs
|
|
14
|
-
* none; their code stays in the page),
|
|
15
|
-
*
|
|
16
|
-
*
|
|
17
|
-
* its
|
|
14
|
+
* none; their code stays in the page), the stylesheets and scripts it loads
|
|
15
|
+
* (`html-assets.ts` says what each is), and what the converter couldn't
|
|
16
|
+
* write as it was. Document-level ones sit above the component, the rest
|
|
17
|
+
* above its `return`, where the planner's own go. A report entry names the
|
|
18
|
+
* page and its line, never the module's.
|
|
19
|
+
*
|
|
20
|
+
* Under `--css bestax` or `--css bulma` the page's link to Bulma's stylesheet
|
|
21
|
+
* becomes `import 'bulma/css/bulma.min.css'` at the top of the module, which
|
|
22
|
+
* the transform's stylesheet pass then retargets exactly as it would in a JSX
|
|
23
|
+
* file, so the CLI's manifest step reads the module the same way too.
|
|
18
24
|
*
|
|
19
25
|
* Nothing is ever overwritten: a module already beside the page stops it
|
|
20
26
|
* (`html-file:exists`), so a second run changes nothing, and the page itself
|
|
@@ -31,8 +37,9 @@ import { childrenOf, isElement, isText, parseHtml, } from '../_shared/html/parse
|
|
|
31
37
|
import { boxOfDisplay, uaDisplay, validInlineDisplay, } from '../_shared/html/whitespace.js';
|
|
32
38
|
import { flaggableRoot } from './class-map.js';
|
|
33
39
|
import { bulmaDisplay, bulmaOutOfFlow, HELPER_DISPLAY } from './display.js';
|
|
40
|
+
import { bulmaBuildMessage, bulmaMediaMessage, bulmaStylesheetMessage, classifyAsset, extensionMessage, fontAwesomeMessage, jqueryMessage, PRIMARY_CLASS, scriptMessage, stylesheetMessage, webfontMessage, } from './html-assets.js';
|
|
34
41
|
import { htmlRuleId, NAVBAR_FIXED_CLASSES, THEME_CLASSES, } from './html-rules.js';
|
|
35
|
-
import { writesTypeScript } from './project.js';
|
|
42
|
+
import { inNextApp, writesTypeScript } from './project.js';
|
|
36
43
|
import transform from './transform.js';
|
|
37
44
|
const HTML_NS = 'http://www.w3.org/1999/xhtml';
|
|
38
45
|
function attributeOf(element, name) {
|
|
@@ -203,7 +210,8 @@ const JAVASCRIPT_TYPES = /^(?:|module|(?:text|application)\/(?:x-)?(?:javascript
|
|
|
203
210
|
const SCRIPT_PATTERNS = [
|
|
204
211
|
[
|
|
205
212
|
'burger',
|
|
206
|
-
|
|
213
|
+
// Bulma 0.x's docs gave the burger a `burger` class beside its own.
|
|
214
|
+
/navbar-burger|['"`][^'"`\n]*\.burger\b/,
|
|
207
215
|
"toggles the navbar burger: bestax's `Navbar.Burger` takes `active` and `onClick`, and `Navbar.Menu` the same `active`, so keep the open state in `useState` and pass it to both",
|
|
208
216
|
],
|
|
209
217
|
[
|
|
@@ -224,30 +232,40 @@ const SCRIPT_PATTERNS = [
|
|
|
224
232
|
[
|
|
225
233
|
'theme',
|
|
226
234
|
/data-theme|dataset\.theme|theme-(?:dark|light)|prefers-color-scheme/,
|
|
227
|
-
"switches Bulma's light and dark schemes: bestax's `Theme` writes `data-theme` on `<html>` from its `colorMode` (`light`, `dark` or `system`), so keep the choice in state and pass it",
|
|
235
|
+
"switches Bulma's light and dark schemes: bestax's `Theme` writes `data-theme` on `<html>` from its `colorMode` (`light`, `dark` or `system`), so keep the choice in state, saved in `localStorage`, and pass it; `Theme` writes the attribute from an effect, after the first paint, so for a saved choice to paint first, keep a small inline script in the `<head>` of the app's index.html that reads it (or the OS's, with none saved) and writes `data-theme` before the app loads, and start the state from what that script wrote",
|
|
228
236
|
],
|
|
229
237
|
];
|
|
238
|
+
/**
|
|
239
|
+
* The patterns a script beside the page is looked over for. A whole file is
|
|
240
|
+
* more than one inline script, so only the ones that name what they drive.
|
|
241
|
+
*/
|
|
242
|
+
const LOCAL_SCRIPT_PATTERNS = SCRIPT_PATTERNS.filter(([token]) => ['burger', 'carousel', 'theme'].includes(token));
|
|
243
|
+
/** Larger than this, a script beside the page is a bundle, not the page's. */
|
|
244
|
+
const LOCAL_SCRIPT_LIMIT = 256 * 1024;
|
|
230
245
|
const SCRIPT_KEPT = "it was dropped, since a component doesn't run a page's scripts, and its code is still in the page";
|
|
231
|
-
|
|
246
|
+
/** A `media` an import can't stand for: anything but every medium. */
|
|
247
|
+
function forSomeMedia(media) {
|
|
248
|
+
const query = media?.trim().toLowerCase() ?? '';
|
|
249
|
+
return query !== '' && query !== 'all';
|
|
250
|
+
}
|
|
251
|
+
function scriptNotes(script, page) {
|
|
232
252
|
const location = script.sourceCodeLocation;
|
|
233
253
|
const line = location?.startLine ?? null;
|
|
234
254
|
const endLine = location?.endLine;
|
|
235
255
|
const src = attributeOf(script, 'src');
|
|
236
|
-
if (src !== undefined) {
|
|
237
|
-
return [
|
|
238
|
-
{
|
|
239
|
-
rule: htmlRuleId('html-asset'),
|
|
240
|
-
message: `the page loads the script \`${quote(src)}\`, and a component runs no page scripts: load it in the app if the app still needs it, or replace what it does with React code`,
|
|
241
|
-
line,
|
|
242
|
-
},
|
|
243
|
-
];
|
|
244
|
-
}
|
|
245
256
|
const type = (attributeOf(script, 'type') ?? '').trim().toLowerCase();
|
|
257
|
+
// A browser loads a `src` only for a script of a JavaScript type; one of
|
|
258
|
+
// any other type (a template, data) is the page's own text, not code.
|
|
259
|
+
if (src !== undefined && JAVASCRIPT_TYPES.test(type)) {
|
|
260
|
+
page.loads.push({ url: src, loads: 'script', line });
|
|
261
|
+
return [];
|
|
262
|
+
}
|
|
263
|
+
const kept = src === undefined ? 'still in the page' : `in \`${quote(src)}\``;
|
|
246
264
|
if (type === 'application/ld+json') {
|
|
247
265
|
return [
|
|
248
266
|
{
|
|
249
267
|
rule: htmlRuleId('html-script', 'json-ld'),
|
|
250
|
-
message: `structured data for search engines (JSON-LD) was dropped with the page's scripts: render it where the app sets each page's head, as \`<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }} />\`; the JSON is
|
|
268
|
+
message: `structured data for search engines (JSON-LD) was dropped with the page's scripts: render it where the app sets each page's head, as \`<script type="application/ld+json" dangerouslySetInnerHTML={{ __html: JSON.stringify(data) }} />\`; the JSON is ${kept}`,
|
|
251
269
|
line,
|
|
252
270
|
endLine,
|
|
253
271
|
},
|
|
@@ -257,7 +275,7 @@ function scriptNotes(script) {
|
|
|
257
275
|
return [
|
|
258
276
|
{
|
|
259
277
|
rule: htmlRuleId('html-script'),
|
|
260
|
-
message: `a \`<script type="${quote(type, 40)}">\` was dropped with the page's scripts: if the page's code reads it (a template, data), move what it holds into the component; it is
|
|
278
|
+
message: `a \`<script type="${quote(type, 40)}">\` was dropped with the page's scripts: if the page's code reads it (a template, data), move what it holds into the component; it is ${kept}`,
|
|
261
279
|
line,
|
|
262
280
|
endLine,
|
|
263
281
|
},
|
|
@@ -283,43 +301,183 @@ function scriptNotes(script) {
|
|
|
283
301
|
}));
|
|
284
302
|
}
|
|
285
303
|
/** Remove every `<script>` from the content, with a note for each. */
|
|
286
|
-
function dropScripts(parent, notes) {
|
|
304
|
+
function dropScripts(parent, notes, page) {
|
|
287
305
|
const nodes = childrenOf(parent);
|
|
288
306
|
for (let i = 0; i < nodes.length; i++) {
|
|
289
307
|
const node = nodes[i];
|
|
290
308
|
if (!isElement(node))
|
|
291
309
|
continue;
|
|
292
310
|
if (node.tagName === 'script' && node.namespaceURI === HTML_NS) {
|
|
293
|
-
notes.push(...scriptNotes(node));
|
|
311
|
+
notes.push(...scriptNotes(node, page));
|
|
294
312
|
nodes.splice(i, 1);
|
|
295
313
|
i--;
|
|
296
314
|
}
|
|
297
315
|
else if (node.tagName !== 'template') {
|
|
298
|
-
dropScripts(node, notes);
|
|
316
|
+
dropScripts(node, notes, page);
|
|
299
317
|
}
|
|
300
318
|
}
|
|
301
319
|
}
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
320
|
+
// ---- Stylesheets and scripts it loads --------------------------------------
|
|
321
|
+
/** The import a page's Bulma `<link>` becomes, before `--css` retargets it. */
|
|
322
|
+
export const BULMA_IMPORT = 'import "bulma/css/bulma.min.css";';
|
|
323
|
+
/**
|
|
324
|
+
* Where a relative URL points, read beside the page: a file there, nothing
|
|
325
|
+
* there, or somewhere the page's folder can't say: any other URL, one a
|
|
326
|
+
* `../` takes out of the page's folder (which isn't beside it, and isn't
|
|
327
|
+
* the codemod's to open), and one whose path can't be read.
|
|
328
|
+
*/
|
|
329
|
+
function placeOf(url, dir) {
|
|
330
|
+
const elsewhere = { place: 'elsewhere', target: url };
|
|
331
|
+
if (!isRelative(url))
|
|
332
|
+
return elsewhere;
|
|
333
|
+
const target = urlPath(url);
|
|
334
|
+
const file = path.resolve(dir, target);
|
|
335
|
+
if (path.relative(dir, file).split(/[/\\]/)[0] === '..')
|
|
336
|
+
return elsewhere;
|
|
337
|
+
let stat;
|
|
338
|
+
try {
|
|
339
|
+
stat = fs.statSync(file, { throwIfNoEntry: false });
|
|
312
340
|
}
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
341
|
+
catch {
|
|
342
|
+
// A folder on the way that can't be searched (`EACCES`), say.
|
|
343
|
+
return elsewhere;
|
|
344
|
+
}
|
|
345
|
+
return stat?.isFile()
|
|
346
|
+
? { place: 'beside', target }
|
|
347
|
+
: { place: 'missing', target };
|
|
348
|
+
}
|
|
349
|
+
/**
|
|
350
|
+
* A script beside the page, looked over the way an inline script is, for
|
|
351
|
+
* the patterns that name what they drive. Null when it can't be read, is too
|
|
352
|
+
* large to be the page's own, or shows none of them.
|
|
353
|
+
*/
|
|
354
|
+
function localScriptNotes(quoted, target, dir, line) {
|
|
355
|
+
let code;
|
|
356
|
+
try {
|
|
357
|
+
const file = path.resolve(dir, target);
|
|
358
|
+
if (fs.statSync(file).size > LOCAL_SCRIPT_LIMIT)
|
|
359
|
+
return null;
|
|
360
|
+
code = fs.readFileSync(file, 'utf8');
|
|
361
|
+
}
|
|
362
|
+
catch {
|
|
363
|
+
return null;
|
|
364
|
+
}
|
|
365
|
+
const found = LOCAL_SCRIPT_PATTERNS.filter(([, pattern]) => pattern.test(code));
|
|
366
|
+
if (found.length === 0)
|
|
367
|
+
return null;
|
|
368
|
+
return found.map(([token, , recipe]) => ({
|
|
369
|
+
rule: htmlRuleId('html-script', token),
|
|
370
|
+
message: `the page's script \`${quoted}\` ${recipe}; a component doesn't load it, and its code is still in \`${target}\``,
|
|
321
371
|
line,
|
|
322
|
-
};
|
|
372
|
+
}));
|
|
373
|
+
}
|
|
374
|
+
/** Whether the page's markup has a primary color class. */
|
|
375
|
+
function usesPrimary(root) {
|
|
376
|
+
return childrenOf(root).some(child => isElement(child) &&
|
|
377
|
+
(classesOf(child).some(token => PRIMARY_CLASS.test(token)) ||
|
|
378
|
+
usesPrimary(child)));
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* The stylesheets and scripts the page loads, as notes: one for each, but
|
|
382
|
+
* one for an extension's stylesheet and script together, since they are one
|
|
383
|
+
* thing to replace. Under `--css bestax` or `--css bulma`, Bulma's standard
|
|
384
|
+
* stylesheet becomes an import in the module (`importsBulma`), which the
|
|
385
|
+
* transform then retargets as it would the same import in a JSX file, and is
|
|
386
|
+
* noted only for what that leaves to do. Not for a link with a media query,
|
|
387
|
+
* which an import can't stand for, nor in a Next.js app, which takes a
|
|
388
|
+
* global stylesheet only in its root layout or `pages/_app`.
|
|
389
|
+
*/
|
|
390
|
+
function assetNotes(page, root) {
|
|
391
|
+
const notes = [];
|
|
392
|
+
const shared = new Map();
|
|
393
|
+
const note = (rule, message, line) => notes.push({ rule, message, line });
|
|
394
|
+
for (const load of page.loads) {
|
|
395
|
+
const asset = classifyAsset(load.url, load.loads);
|
|
396
|
+
const quoted = quote(load.url);
|
|
397
|
+
if (asset.kind === 'bulma' &&
|
|
398
|
+
asset.build === null &&
|
|
399
|
+
forSomeMedia(load.media)) {
|
|
400
|
+
note(htmlRuleId('html-asset', 'bulma'), bulmaMediaMessage(quoted, asset.version, quote(load.media, 40)), load.line);
|
|
401
|
+
continue;
|
|
402
|
+
}
|
|
403
|
+
const key = asset.kind === 'bulma-extension'
|
|
404
|
+
? `extension:${asset.name.toLowerCase()}`
|
|
405
|
+
: asset.kind === 'bulma' && asset.build === null
|
|
406
|
+
? 'bulma'
|
|
407
|
+
: null;
|
|
408
|
+
if (key !== null) {
|
|
409
|
+
const group = shared.get(key);
|
|
410
|
+
if (group)
|
|
411
|
+
group.loads.push(load);
|
|
412
|
+
else
|
|
413
|
+
shared.set(key, { asset, loads: [load] });
|
|
414
|
+
continue;
|
|
415
|
+
}
|
|
416
|
+
switch (asset.kind) {
|
|
417
|
+
case 'bulma':
|
|
418
|
+
note(htmlRuleId('html-asset', 'bulma'), bulmaBuildMessage(asset.build, quoted), load.line);
|
|
419
|
+
break;
|
|
420
|
+
case 'font-awesome':
|
|
421
|
+
note(htmlRuleId('html-asset', 'font-awesome'), fontAwesomeMessage(asset, quoted), load.line);
|
|
422
|
+
break;
|
|
423
|
+
case 'jquery':
|
|
424
|
+
note(htmlRuleId('html-asset', 'jquery'), jqueryMessage(quoted), load.line);
|
|
425
|
+
break;
|
|
426
|
+
case 'webfont':
|
|
427
|
+
note(htmlRuleId('html-asset', 'webfont'), webfontMessage(asset, quoted), load.line);
|
|
428
|
+
break;
|
|
429
|
+
case 'stylesheet': {
|
|
430
|
+
const { place, target } = placeOf(load.url, page.dir);
|
|
431
|
+
note(htmlRuleId('html-asset', 'stylesheet'), stylesheetMessage(quoted, place, quote(target)), load.line);
|
|
432
|
+
break;
|
|
433
|
+
}
|
|
434
|
+
case 'script': {
|
|
435
|
+
const { place, target } = placeOf(load.url, page.dir);
|
|
436
|
+
const local = place === 'beside'
|
|
437
|
+
? localScriptNotes(quoted, target, page.dir, load.line)
|
|
438
|
+
: null;
|
|
439
|
+
if (local)
|
|
440
|
+
notes.push(...local);
|
|
441
|
+
else {
|
|
442
|
+
note(htmlRuleId('html-asset', 'script'), scriptMessage(quoted, place, quote(target)), load.line);
|
|
443
|
+
}
|
|
444
|
+
break;
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
}
|
|
448
|
+
let importsBulma = false;
|
|
449
|
+
for (const { asset, loads } of shared.values()) {
|
|
450
|
+
const lines = [
|
|
451
|
+
...new Set(loads.flatMap(load => (load.line === null ? [] : [load.line]))),
|
|
452
|
+
];
|
|
453
|
+
const quoted = [...new Set(loads.map(load => quote(load.url)))];
|
|
454
|
+
let message;
|
|
455
|
+
let rule;
|
|
456
|
+
if (asset.kind === 'bulma') {
|
|
457
|
+
rule = htmlRuleId('html-asset', 'bulma');
|
|
458
|
+
importsBulma = page.css !== 'keep' && !page.nextjs;
|
|
459
|
+
message = bulmaStylesheetMessage({
|
|
460
|
+
quoted: quoted[0],
|
|
461
|
+
version: asset.version,
|
|
462
|
+
css: page.css,
|
|
463
|
+
usesPrimary: usesPrimary(root),
|
|
464
|
+
nextjs: page.nextjs,
|
|
465
|
+
});
|
|
466
|
+
}
|
|
467
|
+
else {
|
|
468
|
+
rule = htmlRuleId('html-asset', 'bulma-extension');
|
|
469
|
+
message = extensionMessage(asset.name, quoted, new Set(loads.map(load => load.loads)), page.css);
|
|
470
|
+
}
|
|
471
|
+
if (message === null)
|
|
472
|
+
continue;
|
|
473
|
+
notes.push({
|
|
474
|
+
rule,
|
|
475
|
+
message,
|
|
476
|
+
line: loads[0].line,
|
|
477
|
+
...(lines.length > 1 ? { lines } : {}),
|
|
478
|
+
});
|
|
479
|
+
}
|
|
480
|
+
return { notes, importsBulma };
|
|
323
481
|
}
|
|
324
482
|
/** What a `<meta>` is for: its `name`, `property`, `http-equiv` or charset. */
|
|
325
483
|
function metaKind(meta) {
|
|
@@ -336,7 +494,7 @@ function listed(values) {
|
|
|
336
494
|
const shown = unique.slice(0, 6).join(', ');
|
|
337
495
|
return unique.length > 6 ? `${shown}, …` : shown;
|
|
338
496
|
}
|
|
339
|
-
function headNotes(head,
|
|
497
|
+
function headNotes(head, notes, page) {
|
|
340
498
|
const metas = [];
|
|
341
499
|
const links = [];
|
|
342
500
|
for (const node of head) {
|
|
@@ -359,7 +517,12 @@ function headNotes(head, css, notes) {
|
|
|
359
517
|
const rel = (attributeOf(node, 'rel') ?? '').toLowerCase().split(/\s+/);
|
|
360
518
|
const href = attributeOf(node, 'href');
|
|
361
519
|
if (rel.includes('stylesheet') && href !== undefined) {
|
|
362
|
-
|
|
520
|
+
page.loads.push({
|
|
521
|
+
url: href,
|
|
522
|
+
loads: 'stylesheet',
|
|
523
|
+
line,
|
|
524
|
+
media: attributeOf(node, 'media'),
|
|
525
|
+
});
|
|
363
526
|
}
|
|
364
527
|
else {
|
|
365
528
|
links.push(node);
|
|
@@ -375,7 +538,7 @@ function headNotes(head, css, notes) {
|
|
|
375
538
|
});
|
|
376
539
|
break;
|
|
377
540
|
case 'script':
|
|
378
|
-
notes.push(...scriptNotes(node));
|
|
541
|
+
notes.push(...scriptNotes(node, page));
|
|
379
542
|
break;
|
|
380
543
|
case 'base':
|
|
381
544
|
notes.push({
|
|
@@ -757,9 +920,11 @@ function todoLine(note, page) {
|
|
|
757
920
|
const message = note.message.replace(/[\r\n\u2028\u2029]+/g, ' ').trim();
|
|
758
921
|
const at = note.line === null
|
|
759
922
|
? ''
|
|
760
|
-
:
|
|
761
|
-
?
|
|
762
|
-
:
|
|
923
|
+
: note.lines !== undefined
|
|
924
|
+
? ` (${page}:${note.lines.join(', ')})`
|
|
925
|
+
: ` (${page}:${note.line}${note.endLine !== undefined && note.endLine > note.line
|
|
926
|
+
? `-${note.endLine}`
|
|
927
|
+
: ''})`;
|
|
763
928
|
return `// TODO(bestax-migrate): ${message}${at}`;
|
|
764
929
|
}
|
|
765
930
|
/** What a shared line says about several relative URLs, by rule. */
|
|
@@ -882,14 +1047,22 @@ export function convertPage(file, source, collector, options = {}) {
|
|
|
882
1047
|
}, outputPath);
|
|
883
1048
|
}
|
|
884
1049
|
const notes = { document: [], content: [] };
|
|
1050
|
+
const context = {
|
|
1051
|
+
dir: path.dirname(file),
|
|
1052
|
+
css: options.cssMode ?? 'keep',
|
|
1053
|
+
nextjs: inNextApp(file, options.packageLanguages),
|
|
1054
|
+
loads: [],
|
|
1055
|
+
};
|
|
885
1056
|
parseNotes(parsed, notes.document);
|
|
886
|
-
headNotes(parsed.head,
|
|
1057
|
+
headNotes(parsed.head, notes.document, context);
|
|
887
1058
|
const body = parsed.kind === 'document' ? parsed.root : null;
|
|
888
1059
|
documentAttributeNotes('html', body?.parentNode && isElement(body.parentNode)
|
|
889
1060
|
? body.parentNode
|
|
890
1061
|
: undefined, notes.document);
|
|
891
1062
|
documentAttributeNotes('body', body ?? undefined, notes.document);
|
|
892
|
-
dropScripts(parsed.root, notes.document);
|
|
1063
|
+
dropScripts(parsed.root, notes.document, context);
|
|
1064
|
+
const assets = assetNotes(context, parsed.root);
|
|
1065
|
+
notes.document.push(...assets.notes);
|
|
893
1066
|
const emitted = emitJsx(parsed.root, {
|
|
894
1067
|
displayOf: pageDisplayOf,
|
|
895
1068
|
indent: 2,
|
|
@@ -905,6 +1078,8 @@ export function convertPage(file, source, collector, options = {}) {
|
|
|
905
1078
|
const lines = [
|
|
906
1079
|
...header,
|
|
907
1080
|
...(header.length > 0 ? [''] : []),
|
|
1081
|
+
// The transform retargets it under `--css`, as it would in a JSX file.
|
|
1082
|
+
...(assets.importsBulma ? [BULMA_IMPORT] : []),
|
|
908
1083
|
...notes.document.map(note => todoLine(note, page)),
|
|
909
1084
|
`export default function ${componentName(file)}() {`,
|
|
910
1085
|
...contentLines(notes.content, page).map(line => ` ${line}`),
|
|
@@ -7,8 +7,13 @@
|
|
|
7
7
|
* renders exactly the markup the element did. Anything that would change the
|
|
8
8
|
* markup is either left in `className` (a class with no prop here) or refuses
|
|
9
9
|
* the whole element with a TODO (an attribute or tag the component would
|
|
10
|
-
* render differently).
|
|
10
|
+
* render differently). One exception, on purpose: Font Awesome's own
|
|
11
|
+
* `<i aria-hidden="true">` alone in a `.icon` that carries nothing assistive
|
|
12
|
+
* tech reads (the table's `hiddenMovesBeside`) converts to `Icon`, which writes that attribute
|
|
13
|
+
* on the `.icon` instead (`movesHidden`), since the glyph is hidden either
|
|
14
|
+
* way.
|
|
11
15
|
*/
|
|
16
|
+
import { type Glyph } from './glyph.js';
|
|
12
17
|
export interface ElementFacts {
|
|
13
18
|
tag: string;
|
|
14
19
|
/** The static className, split on whitespace, in order. */
|
|
@@ -102,6 +107,26 @@ export interface ElementFacts {
|
|
|
102
107
|
* The string the element's content renders, when it is one static text.
|
|
103
108
|
*/
|
|
104
109
|
text?: string;
|
|
110
|
+
/**
|
|
111
|
+
* The classes the element directly around this one renders: its static
|
|
112
|
+
* ones, or for a bestax component already in the file, its root class and
|
|
113
|
+
* the flags written out as `true`, so a re-run reads the same. What an
|
|
114
|
+
* entry that `readsGlyph` reads to point at `Control`'s icon props.
|
|
115
|
+
*/
|
|
116
|
+
parentTokens?: readonly string[];
|
|
117
|
+
/**
|
|
118
|
+
* The nearest <a> or <button> around this element, a bestax component that
|
|
119
|
+
* renders one included, and what names it: its own `aria-label` or
|
|
120
|
+
* `aria-labelledby`, written out, text in it outside every icon, or `none`
|
|
121
|
+
* when nothing does. `label` is left out when it can't be told from here (a
|
|
122
|
+
* name or content written as an expression, or a component the codemod
|
|
123
|
+
* doesn't know inside). What an entry that `readsGlyph` reads to say
|
|
124
|
+
* whether an icon is decoration there.
|
|
125
|
+
*/
|
|
126
|
+
clickableAround?: {
|
|
127
|
+
tag: string;
|
|
128
|
+
label?: 'aria-label' | 'aria-labelledby' | 'text' | 'none';
|
|
129
|
+
};
|
|
105
130
|
}
|
|
106
131
|
export interface ChildFacts {
|
|
107
132
|
tag: string;
|
|
@@ -178,6 +203,20 @@ export interface Conversion {
|
|
|
178
203
|
* they go, and the element closes itself.
|
|
179
204
|
*/
|
|
180
205
|
rendersChildren?: true;
|
|
206
|
+
/**
|
|
207
|
+
* The glyph the target renders from props in place of the element's only
|
|
208
|
+
* child (`Icon`'s `<i>`), written before the props above. The child goes,
|
|
209
|
+
* as `rendersChildren`, which is set beside it, says.
|
|
210
|
+
*/
|
|
211
|
+
glyph?: Glyph;
|
|
212
|
+
/**
|
|
213
|
+
* That child carried the `aria-hidden` the target writes on the element
|
|
214
|
+
* (Font Awesome's own `<i aria-hidden="true">`), so it moves up: the one
|
|
215
|
+
* way a conversion's markup differs from the element's, on purpose, since
|
|
216
|
+
* the glyph is hidden either way. The render tests compare it with the
|
|
217
|
+
* attribute moved up, and nothing else.
|
|
218
|
+
*/
|
|
219
|
+
movesHidden?: true;
|
|
181
220
|
/**
|
|
182
221
|
* The target renders the bare element around this one too: the component
|
|
183
222
|
* takes that element's place, with its `key`.
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"plan.d.ts","sourceRoot":"","sources":["../../../src/sources/bulma-classes/plan.ts"],"names":[],"mappings":"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"}
|