@astryxdesign/cli 0.7.12-canary.f543ec7 → 0.7.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/CHANGELOG.md +107 -0
- package/api/build/blocks/blocks.d.mts +41 -0
- package/api/build/blocks/blocks.mjs +118 -68
- package/api/component/__fixtures__/resolutions/modules.txt +3 -3
- package/api/docs/_adapter.d.mts +22 -0
- package/api/docs/_adapter.mjs +81 -3
- package/api/docs/docs.doc.mjs +5 -5
- package/api/docs/docs.type.d.mts +21 -0
- package/api/docs/docs.type.mjs +11 -0
- package/api/docs/list/list.d.mts +5 -1
- package/api/docs/list/list.mjs +72 -6
- package/api/docs/node/node.mjs +2 -40
- package/api/doctor/doctor.d.mts +4 -4
- package/api/doctor/doctor.mjs +248 -78
- package/api/icon/_authoring.mjs +8 -1
- package/api/icon/add/add.mjs +45 -5
- package/api/icon/icon.type.d.mts +24 -0
- package/api/icon/icon.type.mjs +6 -0
- package/api/icon/iconAdd.doc.mjs +1 -1
- package/api/icon/map/map.mjs +11 -4
- package/api/index.d.mts +1 -0
- package/api/index.mjs +1 -0
- package/api/integration/add-helpers.d.mts +5 -1
- package/api/integration/add-helpers.mjs +1 -1
- package/api/render/_adapter.mjs +9 -0
- package/api/render/render.doc.mjs +3 -3
- package/api/render/render.type.d.mts +1 -1
- package/api/render/render.type.mjs +1 -1
- package/api/search/ORDERING.md +3 -0
- package/api/search/_adapter.d.mts +32 -3
- package/api/search/_adapter.mjs +50 -22
- package/api/search/_fit-order.d.mts +47 -0
- package/api/search/_fit-order.mjs +257 -0
- package/api/search/_main-searcher.mjs +15 -5
- package/api/search/exportSearchIndex.doc.d.mts +10 -0
- package/api/search/exportSearchIndex.doc.mjs +58 -0
- package/api/search/rankSearchIndex.doc.d.mts +10 -0
- package/api/search/rankSearchIndex.doc.mjs +79 -0
- package/api/search/search-index.d.mts +44 -0
- package/api/search/search-index.mjs +270 -0
- package/api/search/search.type.d.mts +28 -0
- package/api/search/search.type.mjs +17 -0
- package/api/swizzle/_moduleGraph.d.mts +7 -0
- package/api/swizzle/_moduleGraph.mjs +191 -83
- package/api/swizzle/copy/copy.mjs +28 -6
- package/api/template/skeleton/skeleton.mjs +1 -1
- package/api/template/template.mjs +1 -0
- package/assets/codemods/transforms/v0.1.0/drop-xds-prefix-imports.mjs +1 -1
- package/assets/codemods/transforms/v0.1.0/index.mjs +1 -1
- package/assets/discover/official-packages.json +16 -0
- package/assets/docs/styling-overview.doc.mjs +3 -3
- package/assets/docs/tree/icon-sets.doc.mjs +4 -4
- package/assets/docs/tree/render-pages.doc.mjs +3 -3
- package/assets/docs/tree/tailwind.doc.mjs +41 -1
- package/assets/docs/tree/type-scale.doc.mjs +19 -2
- package/assets/templates/blocks/components/Hooks/useInputStatusIconHookUsage.tsx +1 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Hooks/useInteractiveRoleHookUsage.tsx +56 -0
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.doc.mjs +2 -2
- package/assets/templates/blocks/components/Hooks/useStreamingTextHookUsage.tsx +66 -10
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Link/useLinkifyHookUsage.tsx +39 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/PowerSearch/usePowerSearchConfigHookUsage.tsx +83 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/RowsVirtualizationHookUsage.tsx +41 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.doc.mjs +14 -0
- package/assets/templates/blocks/components/Table/useTableColumnSettingsStateHookUsage.tsx +97 -0
- package/assets/templates/pages/table-filter/page.tsx +15 -23
- package/assets/templates/pages/table-grouped/page.tsx +30 -30
- package/assets/templates/pages/table-inbox/page.tsx +3 -6
- package/assets/templates/themes/butter/butterTheme.ts +1 -1
- package/assets/templates/themes/gothic/gothicTheme.ts +16 -0
- package/assets/templates/themes/matcha/matchaTheme.ts +1 -1
- package/assets/templates/themes/neutral/neutralTheme.ts +6 -0
- package/assets/templates/themes/y2k/y2kPaletteRefs.generated.ts +1 -1
- package/assets/templates/themes/y2k/y2kTheme.ts +1 -1
- package/clients/cli/commands/docs.doc.mjs +5 -1
- package/clients/cli/commands/docs.mjs +30 -2
- package/clients/cli/commands/experimental/icon.mjs +25 -4
- package/clients/cli/commands/experimental-render.doc.mjs +1 -1
- package/clients/cli/commands/template.mjs +204 -181
- package/clients/cli/lib/resolve-theme.mjs +16 -7
- package/foundation/discovery/component-discovery.mjs +1 -1
- package/foundation/discovery/hook-discovery.mjs +31 -8
- package/foundation/discovery/icon-set-writer.d.mts +21 -5
- package/foundation/discovery/icon-set-writer.mjs +114 -13
- package/foundation/discovery/template-adapter.mjs +2 -2
- package/foundation/doc-compiler/import.d.mts +5 -1
- package/foundation/doc-compiler/import.mjs +40 -6
- package/foundation/fs/import-url.d.mts +20 -0
- package/foundation/fs/import-url.mjs +22 -0
- package/foundation/fs/module-loader.d.mts +24 -3
- package/foundation/fs/module-loader.mjs +69 -47
- package/foundation/render/charts.d.mts +59 -0
- package/foundation/render/charts.mjs +216 -0
- package/foundation/render/embedded-imports.d.mts +7 -7
- package/foundation/render/embedded-imports.mjs +63 -54
- package/foundation/render/page-host.d.mts +4 -0
- package/foundation/render/page-host.mjs +45 -0
- package/foundation/render/page-worker.mjs +350 -57
- package/foundation/render/render-page.d.mts +11 -6
- package/foundation/render/render-page.mjs +164 -54
- package/foundation/render/ts-paths.d.mts +63 -0
- package/foundation/render/ts-paths.mjs +138 -0
- package/foundation/response/error-codes.doc.mjs +3 -3
- package/foundation/response/error-codes.mjs +3 -2
- package/foundation/text/levenshtein.d.mts +6 -1
- package/foundation/text/levenshtein.mjs +25 -9
- package/foundation/text/suggest-order.d.mts +8 -1
- package/foundation/text/suggest-order.mjs +72 -6
- package/foundation/xle/registry-core.mjs +1 -1
- package/package.json +7 -6
|
@@ -14,6 +14,7 @@ import * as fs from 'node:fs';
|
|
|
14
14
|
import * as path from 'node:path';
|
|
15
15
|
import {isBuiltin} from 'node:module';
|
|
16
16
|
import {packageOf} from '../../foundation/render/served-packages.mjs';
|
|
17
|
+
import {resolveInstalledPackageDir} from '../../foundation/integrations/installed-package.mjs';
|
|
17
18
|
import {parse} from '@babel/parser';
|
|
18
19
|
import {
|
|
19
20
|
inspectPackageExport,
|
|
@@ -390,6 +391,8 @@ function publicSurfaceOf(packageDir, srcRoot, originsOf) {
|
|
|
390
391
|
* file the copy holds (absolute source path), with the edits its imports need
|
|
391
392
|
* @property {Map<string, Set<string>>} dependsOn file -> carried files its
|
|
392
393
|
* computed specifiers point at
|
|
394
|
+
* @property {Map<string, string>} otherPackages the source root of each other
|
|
395
|
+
* Astryx package the copy carries modules from, with its package name
|
|
393
396
|
* @property {Record<string, string>} dependencies each npm package the copied
|
|
394
397
|
* files import, other than the owner package and Node's built-in modules,
|
|
395
398
|
* with the range the owner package declares
|
|
@@ -429,19 +432,6 @@ export function planCopy({
|
|
|
429
432
|
});
|
|
430
433
|
/** @param {string} file @param {string} name */
|
|
431
434
|
const originOf = (file, name) => originsOf(file, 'type').get(name) ?? null;
|
|
432
|
-
/**
|
|
433
|
-
* The subpaths that export a module's export: as a value when the import
|
|
434
|
-
* takes a value, since a type-only export is erased at runtime.
|
|
435
|
-
* @param {string} file
|
|
436
|
-
* @param {string} name
|
|
437
|
-
* @param {boolean} typeOnly
|
|
438
|
-
*/
|
|
439
|
-
const exportersOf = (file, name, typeOnly) => {
|
|
440
|
-
const origin = originOf(file, name);
|
|
441
|
-
if (!origin) return [];
|
|
442
|
-
const kind = typeOnly ? 'type' : 'value';
|
|
443
|
-
return surface.byOrigin[kind].get(`${origin.file}#${origin.name}`) ?? [];
|
|
444
|
-
};
|
|
445
435
|
/**
|
|
446
436
|
* The name a module exports one of its own bindings under, or null.
|
|
447
437
|
* @param {{file: string, name: string}} origin
|
|
@@ -454,7 +444,6 @@ export function planCopy({
|
|
|
454
444
|
};
|
|
455
445
|
/** @type {Map<string, string|null>} */
|
|
456
446
|
const resolutions = new Map();
|
|
457
|
-
const surface = publicSurfaceOf(packageDir, srcRoot, originsOf);
|
|
458
447
|
/** @type {CopyPlan['files']} */
|
|
459
448
|
const files = new Map();
|
|
460
449
|
/** @type {CopyPlan['dependsOn']} */
|
|
@@ -472,73 +461,162 @@ export function planCopy({
|
|
|
472
461
|
};
|
|
473
462
|
/** @param {string} file */
|
|
474
463
|
const inComponent = file => file.startsWith(componentDir + path.sep);
|
|
475
|
-
/** @param {string} file */
|
|
476
|
-
const inSource = file => file.startsWith(srcRoot + path.sep);
|
|
477
464
|
|
|
478
465
|
/**
|
|
479
|
-
*
|
|
480
|
-
* definition. An exactly exported file is its own subpath.
|
|
481
|
-
* @param {string} file
|
|
482
|
-
* @returns {string|null}
|
|
466
|
+
* @typedef {ReturnType<typeof makeContext>} PackageContext
|
|
483
467
|
*/
|
|
484
|
-
const subpathCoveringModule = file => {
|
|
485
|
-
const exact = surface.byFile.get(file);
|
|
486
|
-
if (exact) return exact;
|
|
487
|
-
const names = [...originsOf(file, 'type').keys()];
|
|
488
|
-
if (names.length === 0) return null;
|
|
489
|
-
const values = originsOf(file, 'value');
|
|
490
|
-
const options = names.map(name =>
|
|
491
|
-
exportersOf(file, name, !values.has(name))
|
|
492
|
-
.filter(o => o.name === name)
|
|
493
|
-
.map(o => o.subpath),
|
|
494
|
-
);
|
|
495
|
-
const common = options.reduce((acc, list) =>
|
|
496
|
-
acc.filter(s => list.includes(s)),
|
|
497
|
-
);
|
|
498
|
-
return STYLEX_FILE.test(file) ? null : pickSubpath(common, file);
|
|
499
|
-
};
|
|
500
468
|
|
|
501
469
|
/**
|
|
502
|
-
*
|
|
503
|
-
*
|
|
504
|
-
*
|
|
505
|
-
* no public import.
|
|
506
|
-
* @param {string[]} subpaths subpaths that export the name
|
|
507
|
-
* @param {string} target the module the import names
|
|
508
|
-
* @param {string} [originFile] the module that defines the name
|
|
509
|
-
* @returns {string|null}
|
|
470
|
+
* How references inside one package's source are planned: its public
|
|
471
|
+
* surface and the subpaths a name or module is imported from.
|
|
472
|
+
* @param {{packageDir: string, srcRoot: string, ownerPackage: string}} pkg
|
|
510
473
|
*/
|
|
511
|
-
|
|
512
|
-
const
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
474
|
+
function makeContext({packageDir, srcRoot, ownerPackage}) {
|
|
475
|
+
const surface = publicSurfaceOf(packageDir, srcRoot, originsOf);
|
|
476
|
+
/** @param {string} file */
|
|
477
|
+
const inSource = file => file.startsWith(srcRoot + path.sep);
|
|
478
|
+
/**
|
|
479
|
+
* The subpaths that export a module's export: as a value when the import
|
|
480
|
+
* takes a value, since a type-only export is erased at runtime.
|
|
481
|
+
* @param {string} file
|
|
482
|
+
* @param {string} name
|
|
483
|
+
* @param {boolean} typeOnly
|
|
484
|
+
*/
|
|
485
|
+
const exportersOf = (file, name, typeOnly) => {
|
|
486
|
+
const origin = originOf(file, name);
|
|
487
|
+
if (!origin) return [];
|
|
488
|
+
const kind = typeOnly ? 'type' : 'value';
|
|
489
|
+
return surface.byOrigin[kind].get(`${origin.file}#${origin.name}`) ?? [];
|
|
490
|
+
};
|
|
491
|
+
/**
|
|
492
|
+
* The subpath that exports every name a module exports, with the same
|
|
493
|
+
* definition. An exactly exported file is its own subpath.
|
|
494
|
+
* @param {string} file
|
|
495
|
+
* @returns {string|null}
|
|
496
|
+
*/
|
|
497
|
+
const subpathCoveringModule = file => {
|
|
498
|
+
const exact = surface.byFile.get(file);
|
|
499
|
+
if (exact) return exact;
|
|
500
|
+
const names = [...originsOf(file, 'type').keys()];
|
|
501
|
+
if (names.length === 0) return null;
|
|
502
|
+
const values = originsOf(file, 'value');
|
|
503
|
+
const options = names.map(name =>
|
|
504
|
+
exportersOf(file, name, !values.has(name))
|
|
505
|
+
.filter(o => o.name === name)
|
|
506
|
+
.map(o => o.subpath),
|
|
507
|
+
);
|
|
508
|
+
const common = options.reduce((acc, list) =>
|
|
509
|
+
acc.filter(s => list.includes(s)),
|
|
510
|
+
);
|
|
511
|
+
return STYLEX_FILE.test(file) ? null : pickSubpath(common, file);
|
|
512
|
+
};
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* The public subpath to take a name from. A StyleX module is imported by
|
|
516
|
+
* its own exported path, because the StyleX compiler resolves styles and
|
|
517
|
+
* variables from the real module; when that path is not exported, there is
|
|
518
|
+
* no public import.
|
|
519
|
+
* @param {string[]} subpaths subpaths that export the name
|
|
520
|
+
* @param {string} target the module the import names
|
|
521
|
+
* @param {string} [originFile] the module that defines the name
|
|
522
|
+
* @returns {string|null}
|
|
523
|
+
*/
|
|
524
|
+
const choosePublic = (subpaths, target, originFile) => {
|
|
525
|
+
const exact = originFile ? surface.byFile.get(originFile) : undefined;
|
|
526
|
+
if (exact && subpaths.includes(exact)) return exact;
|
|
527
|
+
if (
|
|
528
|
+
STYLEX_FILE.test(target) ||
|
|
529
|
+
(originFile && STYLEX_FILE.test(originFile))
|
|
530
|
+
)
|
|
531
|
+
return null;
|
|
532
|
+
return pickSubpath(subpaths, target);
|
|
533
|
+
};
|
|
534
|
+
|
|
535
|
+
/**
|
|
536
|
+
* Prefer the subpath of the module's top folder, then the package root.
|
|
537
|
+
* @param {string[]} subpaths
|
|
538
|
+
* @param {string} file
|
|
539
|
+
* @returns {string|null}
|
|
540
|
+
*/
|
|
541
|
+
const pickSubpath = (subpaths, file) => {
|
|
542
|
+
if (subpaths.length === 0) return null;
|
|
543
|
+
const top = `./${toPosix(path.relative(srcRoot, file)).split('/')[0]}`;
|
|
544
|
+
return (
|
|
545
|
+
subpaths.find(s => s === top) ??
|
|
546
|
+
subpaths.find(s => s === '.') ??
|
|
547
|
+
[...subpaths].sort((a, b) => a.length - b.length)[0]
|
|
548
|
+
);
|
|
549
|
+
};
|
|
550
|
+
|
|
551
|
+
/** @param {string} subpath */
|
|
552
|
+
const publicSpecifier = subpath =>
|
|
553
|
+
subpath === '.' ? ownerPackage : `${ownerPackage}/${subpath.slice(2)}`;
|
|
554
|
+
|
|
555
|
+
return {
|
|
556
|
+
packageDir,
|
|
557
|
+
srcRoot,
|
|
558
|
+
ownerPackage,
|
|
559
|
+
surface,
|
|
560
|
+
inSource,
|
|
561
|
+
exportersOf,
|
|
562
|
+
subpathCoveringModule,
|
|
563
|
+
choosePublic,
|
|
564
|
+
publicSpecifier,
|
|
565
|
+
};
|
|
566
|
+
}
|
|
567
|
+
const owner = makeContext({packageDir, srcRoot, ownerPackage});
|
|
568
|
+
/** @type {Map<string, PackageContext>} other packages' contexts, by source root */
|
|
569
|
+
const others = new Map();
|
|
570
|
+
/** @param {string} file */
|
|
571
|
+
const contextOf = file => {
|
|
572
|
+
if (file.startsWith(srcRoot + path.sep)) return owner;
|
|
573
|
+
for (const [root, context] of others) {
|
|
574
|
+
if (file.startsWith(root + path.sep)) return context;
|
|
575
|
+
}
|
|
576
|
+
return owner;
|
|
520
577
|
};
|
|
521
578
|
|
|
522
579
|
/**
|
|
523
|
-
*
|
|
524
|
-
*
|
|
580
|
+
* The source module a bare import of another Astryx package's
|
|
581
|
+
* `./internal/*` path names, or null. That module is carried, and its
|
|
582
|
+
* package gets a context of its own.
|
|
525
583
|
* @param {string} file
|
|
584
|
+
* @param {string} specifier
|
|
526
585
|
* @returns {string|null}
|
|
527
586
|
*/
|
|
528
|
-
const
|
|
529
|
-
|
|
530
|
-
const
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
);
|
|
587
|
+
const internalModuleOf = (file, specifier) => {
|
|
588
|
+
const name = packageOf(specifier);
|
|
589
|
+
const sub = specifier.slice(name.length + 1);
|
|
590
|
+
if (!name.startsWith('@astryxdesign/') || name === ownerPackage)
|
|
591
|
+
return null;
|
|
592
|
+
if (!/^internal(\/|$)/.test(sub)) return null;
|
|
593
|
+
const installed = resolveInstalledPackageDir(name, path.dirname(file));
|
|
594
|
+
if (!installed) return null;
|
|
595
|
+
const dir = fs.realpathSync(installed.packageDir);
|
|
596
|
+
/** @type {{exports?: unknown}} */
|
|
597
|
+
let pkg;
|
|
598
|
+
try {
|
|
599
|
+
pkg = JSON.parse(
|
|
600
|
+
fs.readFileSync(path.join(dir, 'package.json'), 'utf-8'),
|
|
601
|
+
);
|
|
602
|
+
} catch {
|
|
603
|
+
return null;
|
|
604
|
+
}
|
|
605
|
+
const target = sourceExportTarget(pkg, dir, `./${sub}`);
|
|
606
|
+
// A StyleX module's identity is its path, so the copy keeps importing it
|
|
607
|
+
// by its exported path and agrees with the package on it.
|
|
608
|
+
if (!target || STYLEX_FILE.test(target)) return null;
|
|
609
|
+
const src = path.join(dir, 'src');
|
|
610
|
+
const root = target.startsWith(src + path.sep) ? src : dir;
|
|
611
|
+
if (!others.has(root)) {
|
|
612
|
+
others.set(
|
|
613
|
+
root,
|
|
614
|
+
makeContext({packageDir: dir, srcRoot: root, ownerPackage: name}),
|
|
615
|
+
);
|
|
616
|
+
}
|
|
617
|
+
return target;
|
|
536
618
|
};
|
|
537
619
|
|
|
538
|
-
/** @param {string} subpath */
|
|
539
|
-
const publicSpecifier = subpath =>
|
|
540
|
-
subpath === '.' ? ownerPackage : `${ownerPackage}/${subpath.slice(2)}`;
|
|
541
|
-
|
|
542
620
|
/**
|
|
543
621
|
* Carry a module in the copy.
|
|
544
622
|
* @param {string} from
|
|
@@ -560,8 +638,14 @@ export function planCopy({
|
|
|
560
638
|
const edits = [];
|
|
561
639
|
files.set(file, {code: parsed.code, edits});
|
|
562
640
|
if (!CODE_FILE.test(file)) continue;
|
|
641
|
+
const context = contextOf(file);
|
|
563
642
|
for (const ref of parsed.references) {
|
|
564
643
|
if (!ref.source.startsWith('.')) {
|
|
644
|
+
const internal = internalModuleOf(file, ref.source);
|
|
645
|
+
if (internal) {
|
|
646
|
+
edits.push(sourceEdit(parsed.code, ref, carry(file, internal)));
|
|
647
|
+
continue;
|
|
648
|
+
}
|
|
565
649
|
const builtin = ref.source.startsWith('node:') || isBuiltin(ref.source);
|
|
566
650
|
const name = packageOf(ref.source);
|
|
567
651
|
if (!builtin && name !== ownerPackage) packages.add(name);
|
|
@@ -576,44 +660,61 @@ export function planCopy({
|
|
|
576
660
|
else edits.push(sourceEdit(parsed.code, ref, carry(file, target)));
|
|
577
661
|
continue;
|
|
578
662
|
}
|
|
579
|
-
if (!CODE_FILE.test(target) || !inSource(target)) {
|
|
663
|
+
if (!CODE_FILE.test(target) || !context.inSource(target)) {
|
|
580
664
|
// An asset, such as `../../locales/en.json` or a stylesheet, that the
|
|
581
665
|
// package exports by its own path is imported from the package.
|
|
582
|
-
const subpath = `./${toPosix(path.relative(packageDir, target))}`;
|
|
666
|
+
const subpath = `./${toPosix(path.relative(context.packageDir, target))}`;
|
|
583
667
|
if (
|
|
584
|
-
subpathDeclaration(surface.exportsField, subpath).state ===
|
|
668
|
+
subpathDeclaration(context.surface.exportsField, subpath).state ===
|
|
669
|
+
'declared'
|
|
585
670
|
) {
|
|
671
|
+
if (context !== owner) packages.add(context.ownerPackage);
|
|
586
672
|
edits.push(
|
|
587
|
-
sourceEdit(
|
|
673
|
+
sourceEdit(
|
|
674
|
+
parsed.code,
|
|
675
|
+
ref,
|
|
676
|
+
`${context.ownerPackage}/${subpath.slice(2)}`,
|
|
677
|
+
),
|
|
588
678
|
);
|
|
589
679
|
continue;
|
|
590
680
|
}
|
|
591
681
|
// An unexported asset in the source is carried; one outside it is
|
|
592
682
|
// left for the build to report.
|
|
593
|
-
if (!inSource(target)) continue;
|
|
683
|
+
if (!context.inSource(target)) continue;
|
|
594
684
|
edits.push(sourceEdit(parsed.code, ref, carry(file, target)));
|
|
595
685
|
continue;
|
|
596
686
|
}
|
|
597
|
-
edits.push(planReference(file, parsed.code, ref, target));
|
|
687
|
+
edits.push(planReference(context, file, parsed.code, ref, target));
|
|
598
688
|
}
|
|
599
689
|
}
|
|
600
|
-
return {
|
|
690
|
+
return {
|
|
691
|
+
files,
|
|
692
|
+
dependsOn,
|
|
693
|
+
dependencies: declaredRanges(packageDir, packages),
|
|
694
|
+
otherPackages: new Map(
|
|
695
|
+
[...others].map(([root, c]) => [root, c.ownerPackage]),
|
|
696
|
+
),
|
|
697
|
+
};
|
|
601
698
|
|
|
602
699
|
/**
|
|
603
700
|
* Decide one reference that leaves the component folder.
|
|
701
|
+
* @param {PackageContext} context the package the importing file is in
|
|
604
702
|
* @param {string} file
|
|
605
703
|
* @param {string} code
|
|
606
704
|
* @param {ModuleReference} ref
|
|
607
705
|
* @param {string} target
|
|
608
706
|
* @returns {PlannedEdit}
|
|
609
707
|
*/
|
|
610
|
-
function planReference(file, code, ref, target) {
|
|
708
|
+
function planReference(context, file, code, ref, target) {
|
|
709
|
+
// A carried module of another package imports that package's public names
|
|
710
|
+
// from it, so the copy depends on it.
|
|
711
|
+
if (context !== owner) packages.add(context.ownerPackage);
|
|
611
712
|
if (ref.kind !== 'named' && ref.kind !== 'reexportNamed') {
|
|
612
|
-
const covering = subpathCoveringModule(target);
|
|
713
|
+
const covering = context.subpathCoveringModule(target);
|
|
613
714
|
return sourceEdit(
|
|
614
715
|
code,
|
|
615
716
|
ref,
|
|
616
|
-
covering ? publicSpecifier(covering) : carry(file, target),
|
|
717
|
+
covering ? context.publicSpecifier(covering) : carry(file, target),
|
|
617
718
|
);
|
|
618
719
|
}
|
|
619
720
|
/** @type {Array<{choice: SpecifierChoice, specs: ImportSpecifier[]}>} */
|
|
@@ -630,13 +731,20 @@ export function planCopy({
|
|
|
630
731
|
};
|
|
631
732
|
for (const spec of ref.specifiers) {
|
|
632
733
|
if (spec.imported === '*') {
|
|
633
|
-
const covering = subpathCoveringModule(target);
|
|
634
|
-
add(
|
|
734
|
+
const covering = context.subpathCoveringModule(target);
|
|
735
|
+
add(
|
|
736
|
+
covering ? context.publicSpecifier(covering) : carry(file, target),
|
|
737
|
+
spec,
|
|
738
|
+
);
|
|
635
739
|
continue;
|
|
636
740
|
}
|
|
637
741
|
const origin = originOf(target, spec.imported);
|
|
638
|
-
const publicOptions = exportersOf(
|
|
639
|
-
|
|
742
|
+
const publicOptions = context.exportersOf(
|
|
743
|
+
target,
|
|
744
|
+
spec.imported,
|
|
745
|
+
spec.typeOnly,
|
|
746
|
+
);
|
|
747
|
+
const chosen = context.choosePublic(
|
|
640
748
|
publicOptions.map(o => o.subpath),
|
|
641
749
|
target,
|
|
642
750
|
origin?.file,
|
|
@@ -645,7 +753,7 @@ export function planCopy({
|
|
|
645
753
|
const exported = /** @type {{name: string}} */ (
|
|
646
754
|
publicOptions.find(o => o.subpath === chosen)
|
|
647
755
|
).name;
|
|
648
|
-
add(publicSpecifier(chosen), {...spec, imported: exported});
|
|
756
|
+
add(context.publicSpecifier(chosen), {...spec, imported: exported});
|
|
649
757
|
} else {
|
|
650
758
|
// Take the name from the module that defines it, so the copy carries
|
|
651
759
|
// that module and not a whole barrel. An origin names a binding, so
|
|
@@ -654,7 +762,7 @@ export function planCopy({
|
|
|
654
762
|
const definingExport =
|
|
655
763
|
origin &&
|
|
656
764
|
origin.file !== target &&
|
|
657
|
-
inSource(origin.file) &&
|
|
765
|
+
context.inSource(origin.file) &&
|
|
658
766
|
!parseModule(target).declares.has(spec.imported)
|
|
659
767
|
? exportNameOf(origin)
|
|
660
768
|
: null;
|
|
@@ -361,12 +361,32 @@ export async function swizzleCopy(component, options = {}) {
|
|
|
361
361
|
);
|
|
362
362
|
/** @type {Map<string, string>} */
|
|
363
363
|
const rootDests = new Map();
|
|
364
|
+
// A module from another Astryx package goes under a folder named after that
|
|
365
|
+
// package, at its path in that package's source.
|
|
366
|
+
/** @param {string} file */
|
|
367
|
+
const otherPackageOf = file => {
|
|
368
|
+
for (const [root, name] of plan.otherPackages) {
|
|
369
|
+
if (file.startsWith(root + path.sep)) return {root, folder: name.split('/').pop() ?? name};
|
|
370
|
+
}
|
|
371
|
+
return null;
|
|
372
|
+
};
|
|
373
|
+
/** @param {string} file the version of the package a helper comes from */
|
|
374
|
+
const versionOf = file => {
|
|
375
|
+
const other = otherPackageOf(file);
|
|
376
|
+
return other
|
|
377
|
+
? packageVersionOf(findPackageRoot(other.root) ?? other.root)
|
|
378
|
+
: ownerVersion;
|
|
379
|
+
};
|
|
364
380
|
for (const file of [...allFiles].sort()) {
|
|
365
381
|
if (layoutDestOf(file) != null) continue;
|
|
366
|
-
const
|
|
367
|
-
|
|
382
|
+
const other = otherPackageOf(file);
|
|
383
|
+
const base = other ? other.folder : path.basename(packageDir);
|
|
384
|
+
const packagePath = path.relative(other ? other.root : graphRoot, file);
|
|
385
|
+
let dest = other
|
|
386
|
+
? path.join(outputBase, base, packagePath)
|
|
387
|
+
: path.join(outputBase, packagePath);
|
|
368
388
|
for (let n = 1; takenDests.has(dest); n++) {
|
|
369
|
-
const folder = n === 1 ?
|
|
389
|
+
const folder = n === 1 && !other ? base : `${base}-${n}`;
|
|
370
390
|
dest = path.join(outputBase, folder, packagePath);
|
|
371
391
|
}
|
|
372
392
|
takenDests.add(dest);
|
|
@@ -381,15 +401,17 @@ export async function swizzleCopy(component, options = {}) {
|
|
|
381
401
|
// A private attribute owner module and the unexported style modules it
|
|
382
402
|
// imports travel whole and are replaced only with --overwrite.
|
|
383
403
|
const ownerGroup = new Set();
|
|
404
|
+
/** @param {string} file the source root a carried module sits under */
|
|
405
|
+
const rootOf = file => otherPackageOf(file)?.root ?? srcRoot;
|
|
384
406
|
const ownerQueue = helpers.filter(f =>
|
|
385
|
-
OWNER_MODULE.test(toModulePath(
|
|
407
|
+
OWNER_MODULE.test(toModulePath(rootOf(f), f)),
|
|
386
408
|
);
|
|
387
409
|
while (ownerQueue.length > 0) {
|
|
388
410
|
const file = /** @type {string} */ (ownerQueue.shift());
|
|
389
411
|
if (ownerGroup.has(file)) continue;
|
|
390
412
|
ownerGroup.add(file);
|
|
391
413
|
for (const dep of plan.dependsOn.get(file) ?? []) {
|
|
392
|
-
if (!isComponentFile(dep) && STYLEX_MODULE.test(toModulePath(
|
|
414
|
+
if (!isComponentFile(dep) && STYLEX_MODULE.test(toModulePath(rootOf(dep), dep))) {
|
|
393
415
|
ownerQueue.push(dep);
|
|
394
416
|
}
|
|
395
417
|
}
|
|
@@ -431,7 +453,7 @@ export async function swizzleCopy(component, options = {}) {
|
|
|
431
453
|
attempts.set(file, attempt);
|
|
432
454
|
redirected.set(
|
|
433
455
|
file,
|
|
434
|
-
versionedPath(baseDestOf(file),
|
|
456
|
+
versionedPath(baseDestOf(file), versionOf(file), attempt),
|
|
435
457
|
);
|
|
436
458
|
moved = true;
|
|
437
459
|
}
|
|
@@ -146,7 +146,7 @@ function extractSkeleton(source) {
|
|
|
146
146
|
|
|
147
147
|
// Match a JSX opening tag, e.g. `<Section` or the legacy `<XDSSection`.
|
|
148
148
|
// The `XDS` prefix is optional (templates author bare names post
|
|
149
|
-
// un-prefix migration
|
|
149
|
+
// un-prefix migration). `tagName` is the full authored name
|
|
150
150
|
// (used for the self-closing lookahead); `comp` is the bare display name.
|
|
151
151
|
const openMatch = t.match(/^<((XDS)?([A-Z]\w+))/);
|
|
152
152
|
if (openMatch && !t.startsWith('</')) {
|
|
@@ -193,6 +193,7 @@ export async function template(name, options = {}) {
|
|
|
193
193
|
`Unknown template "${name}"`,
|
|
194
194
|
orderByAffinity(name, templates, t => [t.dirName, t.name], {
|
|
195
195
|
closest: true,
|
|
196
|
+
typos: true,
|
|
196
197
|
}).map(t => ({
|
|
197
198
|
name: t.dirName,
|
|
198
199
|
reason: `${t.type} template`,
|
|
@@ -3,7 +3,7 @@
|
|
|
3
3
|
/**
|
|
4
4
|
* @file Codemod: Drop the `XDS` prefix from @xds/core imports and their usages
|
|
5
5
|
*
|
|
6
|
-
* Part of the XDS-prefix migration
|
|
6
|
+
* Part of the XDS-prefix migration. Rewrites consumer code from
|
|
7
7
|
* the prefixed names to their bare equivalents:
|
|
8
8
|
*
|
|
9
9
|
* XDSButton -> Button
|
|
@@ -24,7 +24,7 @@ import migrateXdsCssSurfaces, {
|
|
|
24
24
|
|
|
25
25
|
export default [
|
|
26
26
|
{
|
|
27
|
-
// XDS-prefix migration
|
|
27
|
+
// XDS-prefix migration. Mandatory in v0.1.0: the release
|
|
28
28
|
// dropped the prefixed compatibility aliases (useXDSTheme, XDSButton,
|
|
29
29
|
// XDSIconRegistry, ...), so consumers upgrading from 0.0.x MUST rewrite
|
|
30
30
|
// prefixed imports to their bare names.
|
|
@@ -100,6 +100,10 @@
|
|
|
100
100
|
"kind": "component",
|
|
101
101
|
"name": "ChatUnreadDivider"
|
|
102
102
|
},
|
|
103
|
+
{
|
|
104
|
+
"kind": "component",
|
|
105
|
+
"name": "ChatVirtualizer"
|
|
106
|
+
},
|
|
103
107
|
{
|
|
104
108
|
"kind": "component",
|
|
105
109
|
"name": "CircleCheckIndicator"
|
|
@@ -128,6 +132,10 @@
|
|
|
128
132
|
"kind": "component",
|
|
129
133
|
"name": "CommentThread"
|
|
130
134
|
},
|
|
135
|
+
{
|
|
136
|
+
"kind": "component",
|
|
137
|
+
"name": "DialogHeroHeader"
|
|
138
|
+
},
|
|
131
139
|
{
|
|
132
140
|
"kind": "component",
|
|
133
141
|
"name": "DiffViewer"
|
|
@@ -136,6 +144,10 @@
|
|
|
136
144
|
"kind": "component",
|
|
137
145
|
"name": "EmojiPicker"
|
|
138
146
|
},
|
|
147
|
+
{
|
|
148
|
+
"kind": "component",
|
|
149
|
+
"name": "LineClamp"
|
|
150
|
+
},
|
|
139
151
|
{
|
|
140
152
|
"kind": "component",
|
|
141
153
|
"name": "ListInput"
|
|
@@ -252,6 +264,10 @@
|
|
|
252
264
|
"kind": "template",
|
|
253
265
|
"name": "CommentThreadShowcase"
|
|
254
266
|
},
|
|
267
|
+
{
|
|
268
|
+
"kind": "template",
|
|
269
|
+
"name": "DialogHeroHeaderShowcase"
|
|
270
|
+
},
|
|
255
271
|
{
|
|
256
272
|
"kind": "template",
|
|
257
273
|
"name": "DiffViewerShowcase"
|
|
@@ -90,7 +90,7 @@ export const docs = {
|
|
|
90
90
|
{
|
|
91
91
|
type: 'code',
|
|
92
92
|
lang: 'bash',
|
|
93
|
-
label: 'Install
|
|
93
|
+
label: 'Install',
|
|
94
94
|
code: '{@pm install @astryxdesign/core @stylexjs/stylex @astryxdesign/theme-neutral}',
|
|
95
95
|
},
|
|
96
96
|
{
|
|
@@ -113,7 +113,7 @@ export const docs = {
|
|
|
113
113
|
},
|
|
114
114
|
{
|
|
115
115
|
type: 'prose',
|
|
116
|
-
text: 'For Tailwind v3, see the layer workaround in {@link generic:migration-setup}.',
|
|
116
|
+
text: 'A project with Tailwind already set up needs a smaller change, which {@link generic:tailwind} shows. For Tailwind v3, see the layer workaround in {@link generic:migration-setup}.',
|
|
117
117
|
},
|
|
118
118
|
],
|
|
119
119
|
},
|
|
@@ -162,7 +162,7 @@ export const docs = {
|
|
|
162
162
|
},
|
|
163
163
|
{
|
|
164
164
|
type: 'prose',
|
|
165
|
-
text:
|
|
165
|
+
text: "`{@command swizzle <Component>}` writes an `astryx-swizzle.json` marker into an output folder it owns (one the copy creates, an empty one, or one it marked before); every module under that folder then compiles as Astryx source, so keep copies in a folder of their own, such as the default `components/astryx`. For a folder that already holds other files it writes no marker and says how to add one by hand. With `@astryxdesign/build`'s Babel, PostCSS and Vite integrations, or `withAstryx()` under webpack, a copy under that folder compiles into `astryx-base`, below `astryx-theme`, so theme component overrides reach it as they reach the package component. Under Turbopack, and with the stock StyleX plugins above, a copy's rules land in your own styles, above `astryx-theme`, and theme component overrides do not reach it.",
|
|
166
166
|
},
|
|
167
167
|
],
|
|
168
168
|
},
|
|
@@ -61,7 +61,7 @@ export const docs = {
|
|
|
61
61
|
},
|
|
62
62
|
{
|
|
63
63
|
type: 'prose',
|
|
64
|
-
text: "`outline.doc.mjs` is the descriptor: name, display name and description (`--display-name`, `--description`), and `license`, the SPDX license of the artwork (it starts as your package's own). `outline.icons.json` starts with no icons and an empty `map`; its `module` is your package name. `outline.generated.js` is the set's generated module, with its declaration file `outline.generated.d.ts`: the CLI writes both, and every icon and the theme map are exports of it. Every field is in {@link schema:icon-set-doc} and {@link schema:icon-set-data}.",
|
|
64
|
+
text: "`outline.doc.mjs` is the descriptor: name, display name and description (`--display-name`, `--description`), and `license`, the SPDX license of the artwork (it starts as your package's own). `outline.icons.json` starts with no icons and an empty `map`; its `module` is your package name. After you rename the package, the next command that writes the data file (`new-set` on the existing set, with or without `--from`, `add`, `map`, or `new-definition --icon`) writes the new name and keeps any subpath (`@acme/old-icons/outline` becomes `@acme/new-icons/outline`), and its receipt says so. `outline.generated.js` is the set's generated module, with its declaration file `outline.generated.d.ts`: the CLI writes both, and every icon and the theme map are exports of it. Every field is in {@link schema:icon-set-doc} and {@link schema:icon-set-data}.",
|
|
65
65
|
},
|
|
66
66
|
{
|
|
67
67
|
type: 'prose',
|
|
@@ -121,7 +121,7 @@ export const docs = {
|
|
|
121
121
|
},
|
|
122
122
|
{
|
|
123
123
|
type: 'prose',
|
|
124
|
-
text: "Every component the library exports becomes an icon (an alias of one component is recorded once), named `<Name>Icon`, with an id from its name (`Squares2X2Icon` is `squares-2x2`: a dimension stays one word). `map` is filled only where a definition's name is an icon's id (`delete` → icon `delete`), and the receipt lists those entries under Mapped by name: a library can draw another meaning under the same name (one library's `delete` is a backspace key), so check each. An icon whose id is only one of a definition's words (`trash` for `delete`) may mean something else in this library, so the receipt lists it under Suggestions, with the `experimental icon map <definition> <icon>` command for each, and writes nothing for it: check the meaning, then run the ones that fit. Run it again after a library release to refresh the icons that library supplies; `map` entries whose icon still exists are kept, and the receipt names any icon the library dropped, any icon `--exclude` left out, and the `map` entries each took with it. Icons you added from your own files or another library are left as they are. Icons you added one file at a time from the same package (`--from <package>/dist/XIcon`) move to the root import when you refresh from the package root and the root exports the same icon; each keeps its id, export, `map` entries, variants, and words on that refresh and every later one, and the receipt lists it under `moved`. A library icon whose id or name a moved icon already has is left out. When another icon of the set already reads the same root export, the icon stays on its own file, and the receipt says why. An icon you already have is known by the library export it re-exports, so it keeps its export name exactly, its `map` entries, variants, and words, even when a newer CLI spells its id differently or your `--exclude` lets a library prefix come off new ids; the receipt lists each changed id under `renamed`. An id you chose that is not a spelling of the library name (`dismiss` for `XIcon`) is kept. A refresh with `--dry-run` writes nothing and prints the Mapped by name and Suggestions lists again; `--display-name` and `--description` on a refresh rewrite the descriptor. Variants added with `add --variant … --all` are kept too; run that `add` again to pick up icons the release added. `experimental icon --list` shows how many definitions the set maps.",
|
|
124
|
+
text: "Every component the library exports becomes an icon (an alias of one component is recorded once), named `<Name>Icon`, with an id from its name (`Squares2X2Icon` is `squares-2x2`: a dimension stays one word). An export name cannot start with a digit, so an icon whose id would start with one once the library prefix comes off keeps the prefix (`fa0` is id `fa-0`, exported as `Fa0Icon`), and an id you give with `--id` must start with a letter. `map` is filled only where a definition's name is an icon's id (`delete` → icon `delete`), and the receipt lists those entries under Mapped by name: a library can draw another meaning under the same name (one library's `delete` is a backspace key), so check each. An icon whose id is only one of a definition's words (`trash` for `delete`) may mean something else in this library, so the receipt lists it under Suggestions, with the `experimental icon map <definition> <icon>` command for each, and writes nothing for it: check the meaning, then run the ones that fit. Run it again after a library release to refresh the icons that library supplies; `map` entries whose icon still exists are kept, and the receipt names any icon the library dropped, any icon `--exclude` left out, and the `map` entries each took with it. Icons you added from your own files or another library are left as they are. Icons you added one file at a time from the same package (`--from <package>/dist/XIcon`) move to the root import when you refresh from the package root and the root exports the same icon; each keeps its id, export, `map` entries, variants, and words on that refresh and every later one, and the receipt lists it under `moved`. A library icon whose id or name a moved icon already has is left out. When another icon of the set already reads the same root export, the icon stays on its own file, and the receipt says why. An icon you already have is known by the library export it re-exports, so it keeps its export name exactly, its `map` entries, variants, and words, even when a newer CLI spells its id differently or your `--exclude` lets a library prefix come off new ids; the receipt lists each changed id under `renamed`. An id you chose that is not a spelling of the library name (`dismiss` for `XIcon`) is kept. A refresh with `--dry-run` writes nothing and prints the Mapped by name and Suggestions lists again; `--display-name` and `--description` on a refresh rewrite the descriptor. Variants added with `add --variant … --all` are kept too; run that `add` again to pick up icons the release added. `experimental icon --list` shows how many definitions the set maps.",
|
|
125
125
|
},
|
|
126
126
|
{
|
|
127
127
|
type: 'prose',
|
|
@@ -139,11 +139,11 @@ astryx experimental icon add --variant solid --from @heroicons/react/24/solid --
|
|
|
139
139
|
},
|
|
140
140
|
{
|
|
141
141
|
type: 'prose',
|
|
142
|
-
text: "A library that redraws its artwork for small sizes (Heroicons' 20px mini and 16px micro) ships each of those as its own set in its own package (their export names match the 24px ones), not as variants: a variant's size comes from CSS. `--variants` lists the variants; `--default-variant` names the one each icon's own export draws (when omitted, the look a library that names its looks draws; otherwise the set's current default, else, on a set without icons, the first variant; a set with icons changes it only with `--from`, and only in a library that names its looks). `--all` adds the variant for every icon whose id an export of `--from` has (`TrashIcon` in the solid module becomes `TrashSolidIcon`, the solid variant of `trash`), and the receipt lists the icons the module has no export for. Running it again changes nothing.",
|
|
142
|
+
text: "A library that redraws its artwork for small sizes (Heroicons' 20px mini and 16px micro) ships each of those as its own set in its own package (their export names match the 24px ones), not as variants: a variant's size comes from CSS. `--variants` lists the variants; `--default-variant` names the one each icon's own export draws (when omitted, the look a library that names its looks draws; otherwise the set's current default, else, on a set without icons, the first variant; a set with icons changes it only with `--from`, and only in a library that names its looks). `--all` adds the variant for every icon whose id an export of `--from` has (`TrashIcon` in the solid module becomes `TrashSolidIcon`, the solid variant of `trash`), and the receipt lists the icons the module has no export for. A library may ship each look as its own package (Font Awesome's solid and regular packages): `--from` may then name the other package, and an icon gains the variant from the export of the same name there; the receipt lists the icons that package has no export for. Running it again changes nothing.",
|
|
143
143
|
},
|
|
144
144
|
{
|
|
145
145
|
type: 'prose',
|
|
146
|
-
text: 'A library whose icons are data (objects or SVG strings) has no components to find: create the set, declare an `adapter` in its data file (see library shapes), and run the command again; every data export then becomes an icon through the adapter. When every icon export shares a leading word, such as `feather` in `featherEye` and `featherSun`, ids and export names leave it out: `featherEye` becomes id `eye`, exported as `EyeIcon`. The prefix is read over icon-like exports only: SCREAMING_CASE constants (`README`, `VERSION`), number and boolean exports, and, next to components or icon objects, plain strings are never icons. Components named `…Provider` or `…Context` are never icons either
|
|
146
|
+
text: 'A library whose icons are data (objects or SVG strings) has no components to find: create the set, declare an `adapter` in its data file (see library shapes), and run the command again; every data export then becomes an icon through the adapter. When every icon export shares a leading word, such as `feather` in `featherEye` and `featherSun`, ids and export names leave it out: `featherEye` becomes id `eye`, exported as `EyeIcon`. The prefix is read over icon-like exports only: SCREAMING_CASE constants (`README`, `VERSION`), number and boolean exports, and, next to components or icon objects, plain strings are never icons. Components named `…Provider` or `…Context` are never icons either, and neither is a pack: an object whose keys are all other exports of the module, such as `fas` next to `faXmark`. Leave out any other export that is not an icon with `--exclude meta,version`: the data file keeps the list in `exclude`, so a refresh leaves them out too. When a library exports one icon under several names (`Trash2` and `Trash`), the icon is recorded once and its other names become words, so `experimental icon trash-2` finds it and `experimental icon map delete trash-2` maps it. `experimental icon add` reads the prefix the same way, and reuses the icon an export already has, so its commands need no `--id`.',
|
|
147
147
|
},
|
|
148
148
|
],
|
|
149
149
|
},
|
|
@@ -57,7 +57,7 @@ export const docs = {
|
|
|
57
57
|
},
|
|
58
58
|
{
|
|
59
59
|
type: 'prose',
|
|
60
|
-
text: 'What it cannot keep: anything that needs a browser while running. State, effects, click handlers and data loaded after the first paint are gone
|
|
60
|
+
text: 'What it cannot keep: anything that needs a browser while running. State, effects, click handlers and data loaded after the first paint are gone. Charts made with recharts are the exception: the render draws each chart and ResponsiveContainer a module of the page (not an installed package) imports from recharts by name (`import {LineChart} from \'recharts\'`), inside the page, so a custom tick, label or shape sees the page\'s React context. A chart sized by its container scales to the width the page gives it, and its legend stays out of the picture: a horizontal legend takes its own row under it (above it when the legend asks for the top), and a vertical legend on one side sits beside it, on that side; in a chart of fixed size, a legend keeps the place its props give it. Charts show the data and props of the page\'s first render, before its effects run: when the page cannot be drawn again without a browser, or its effects change its charts or re-render the component that makes them, each chart is drawn by itself and its parts see React context defaults. A value a chart\'s parts read from a React context that an effect changes may still show its later value. A `chart-size` warning counts the charts the render cannot draw (one used through `import * as R`, say), and says when charts were drawn by themselves. Read browser-only values such as window inside an effect, or the render stops with ERR_RENDER_BROWSER_API.',
|
|
61
61
|
},
|
|
62
62
|
],
|
|
63
63
|
},
|
|
@@ -67,7 +67,7 @@ export const docs = {
|
|
|
67
67
|
content: [
|
|
68
68
|
{
|
|
69
69
|
type: 'prose',
|
|
70
|
-
text: 'Each warning names something that may look different from what you meant, and what to change. The most common is a component that ignores its children: `<ListItem>Save</ListItem>` renders an empty row because ListItem takes `label="Save"`. Others flag remote images (a sandboxed viewer cannot load them; save the image next to the page and import it), a `<script>`, `<link>` or `<iframe>` the page itself renders (the file promises to run and load nothing, so remove them), missing images, and output the page printed while rendering.',
|
|
70
|
+
text: 'Each warning names something that may look different from what you meant, and what to change. The most common is a component that ignores its children: `<ListItem>Save</ListItem>` renders an empty row because ListItem takes `label="Save"`. Others flag remote images (a sandboxed viewer cannot load them; save the image next to the page and import it), a `<script>`, `<link>` or `<iframe>` the page itself renders (the file promises to run and load nothing, so remove them), missing images, and output the page printed while rendering. A `node-crash` warning names a Node build that crashes in `Intl.Segmenter` (its version and path). The page rendered without it, so the output may differ slightly: initials and character counts (such as Avatar initials) can split a joined emoji, a flag, or a letter written with a separate accent mark. Names in ordinary composed letters (`José`, `Zoë`, `李小龙`) come out the same. Render with an official Node.js build to remove the difference.',
|
|
71
71
|
},
|
|
72
72
|
],
|
|
73
73
|
},
|
|
@@ -86,7 +86,7 @@ export const docs = {
|
|
|
86
86
|
},
|
|
87
87
|
{
|
|
88
88
|
type: 'prose',
|
|
89
|
-
text: 'It writes the page, the modules, stylesheets and JSON files the page reaches through its own imports, and the images, fonts and media the render carried (however the page named them, as long as their bytes really are one): each goes where the page\'s relative imports find it from wherever you put the page, and a file the page imports through a path alias (`@/components/Header`) goes where your project\'s `tsconfig.json` or `jsconfig.json` alias points. Every file but the page goes inside the one new folder the restore creates (the topmost folder on the page\'s path that does not exist yet), never into a folder that already exists: a page that imports other files comes back into a new folder (`--out restored/app/page.tsx`), and a file it imports through a path alias comes back only when that alias points inside the new folder. Nor does it write a file that a test, story or benchmark runner would run by its default patterns (Jest, Vitest, Bun, AVA, Playwright, Storybook, Cypress and `node --test`: names such as `*.test.*`, `*.spec.*`, `*_test.*`, `*-test.*`, `*.bench.*`, `*.stories.*`, `*.cy.*`, and anything under `test/`, `tests/` or `__tests__/`), judged from the repository root (every folder above the file up to it), else the topmost folder with a package.json. Restored files are the sender\'s code: read them before you run or import them. Without `--out`, everything comes back into a new folder beside the HTML file, named after it (`page.html` comes back into `page-restored/`). A received file that carries anything else (a file the page never imports, a dotfile, `package.json`, a tool configuration, a path that leaves the folder) is refused with nothing written. An existing file is never replaced unless you pass `--overwrite`, and the refusal names every one; a generated, vendored or ignored file, and any path through a symbolic link, is not written even then. Add `--json` for the result as data: what was written, how big it is, and every warning.',
|
|
89
|
+
text: 'It writes the page, the modules, stylesheets and JSON files the page reaches through its own imports, and the images, fonts and media the render carried (however the page named them, as long as their bytes really are one): each goes where the page\'s relative imports find it from wherever you put the page, and a file the page imports through a path alias (`@/components/Header`) goes where your project\'s `tsconfig.json` or `jsconfig.json` alias points. Every file but the page goes inside the one new folder the restore creates (the topmost folder on the page\'s path that does not exist yet), never into a folder that already exists: a page that imports other files comes back into a new folder (`--out restored/app/page.tsx`), and a file it imports through a path alias comes back only when that alias points inside the new folder. Nor does it write a file that a test, story or benchmark runner would run by its default patterns (Jest, Vitest, Bun, AVA, Playwright, Storybook, Cypress and `node --test`: names such as `*.test.*`, `*.spec.*`, `*_test.*`, `*-test.*`, `*.bench.*`, `*.stories.*`, `*.cy.*`, and anything under `test/`, `tests/` or `__tests__/`), judged from the repository root (every folder above the file up to it), else the topmost folder with a package.json. Restored files are the sender\'s code: read them before you run or import them. Without `--out`, everything comes back into a new folder beside the HTML file, named after it (`page.html` comes back into `page-restored/`). A received file that carries anything else (a file the page never imports, a dotfile, `package.json`, a tool configuration, a path that leaves the folder) is refused with nothing written. A rendered file that lost its embedded source on the way (a sanitizer or an editor strips `<script>` blocks) and has no Astryx components to rebuild from is refused with `ERR_RENDER_SOURCE_DAMAGED`. An existing file is never replaced unless you pass `--overwrite`, and the refusal names every one; a generated, vendored or ignored file, and any path through a symbolic link, is not written even then. Add `--json` for the result as data: what was written, how big it is, and every warning.',
|
|
90
90
|
},
|
|
91
91
|
],
|
|
92
92
|
},
|