@rafters/veneer 0.0.2 → 0.0.4
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/bin.mjs
CHANGED
|
@@ -1,20 +1,54 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import {
|
|
2
|
+
import { i as relativeFile } from "./lint-Bqu-nKvP.mjs";
|
|
3
|
+
import { t as check } from "./check-vsAQPYCD.mjs";
|
|
4
|
+
import path from "node:path";
|
|
5
|
+
import { existsSync, realpathSync } from "node:fs";
|
|
6
|
+
import { parseArgs } from "node:util";
|
|
7
|
+
import { text } from "node:stream/consumers";
|
|
3
8
|
//#region src/bin.ts
|
|
4
|
-
const
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
9
|
+
const COULD_NOT_RUN = 2;
|
|
10
|
+
function parsedArguments() {
|
|
11
|
+
try {
|
|
12
|
+
const { values, positionals } = parseArgs({
|
|
13
|
+
allowPositionals: true,
|
|
14
|
+
options: { stdin: { type: "string" } }
|
|
15
|
+
});
|
|
16
|
+
const [root, ...rest] = positionals;
|
|
17
|
+
return rest.length > 0 ? void 0 : {
|
|
18
|
+
root,
|
|
19
|
+
stdinPath: values.stdin
|
|
20
|
+
};
|
|
21
|
+
} catch {
|
|
22
|
+
return;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
function resolvedLinks(file) {
|
|
26
|
+
if (existsSync(file)) return realpathSync(file);
|
|
27
|
+
const parent = path.dirname(file);
|
|
28
|
+
return parent === file ? file : path.join(resolvedLinks(parent), path.basename(file));
|
|
29
|
+
}
|
|
30
|
+
async function run(root, stdinPath) {
|
|
31
|
+
const file = stdinPath && relativeFile(resolvedLinks(root), resolvedLinks(path.resolve(stdinPath)));
|
|
32
|
+
const unsaved = file === void 0 ? void 0 : { [file]: await text(process.stdin) };
|
|
33
|
+
const result = await check(root, { unsaved });
|
|
34
|
+
if (!result.ok) {
|
|
15
35
|
for (const { message } of result.errors) process.stderr.write(`${message}\n`);
|
|
16
|
-
|
|
36
|
+
return COULD_NOT_RUN;
|
|
17
37
|
}
|
|
38
|
+
process.stdout.write(result.report);
|
|
39
|
+
for (const { message } of result.notices) process.stderr.write(`${message}\n`);
|
|
40
|
+
return result.diagnostics.some(({ severity }) => severity === "error") ? 1 : 0;
|
|
41
|
+
}
|
|
42
|
+
const parsed = parsedArguments();
|
|
43
|
+
if (parsed) try {
|
|
44
|
+
process.exitCode = await run(parsed.root ?? process.cwd(), parsed.stdinPath);
|
|
45
|
+
} catch (error) {
|
|
46
|
+
process.stderr.write(`${error instanceof Error ? error.message : String(error)}\n`);
|
|
47
|
+
process.exitCode = COULD_NOT_RUN;
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
process.stderr.write("usage: rafters-check [root] [--stdin <path>]\n");
|
|
51
|
+
process.exitCode = COULD_NOT_RUN;
|
|
18
52
|
}
|
|
19
53
|
//#endregion
|
|
20
54
|
export {};
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { a as
|
|
1
|
+
import { a as resolveComponents, c as readConfig, n as report, o as ProjectError, r as compareCodePoints, s as openProject, t as lint } from "./lint-Bqu-nKvP.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { mkdir, rename, rm, writeFile } from "node:fs/promises";
|
|
4
4
|
import { lstatSync } from "node:fs";
|
|
@@ -139,6 +139,7 @@ async function check(projectRoot, options = {}) {
|
|
|
139
139
|
const asked = options.paths ?? (unsavedFiles.length > 0 ? unsavedFiles : void 0);
|
|
140
140
|
const diagnostics = await lint(project, {
|
|
141
141
|
componentFolders: config.componentFolders,
|
|
142
|
+
scan: config.scan,
|
|
142
143
|
files: isSave ? void 0 : asked?.filter((file) => !outside.includes(file))
|
|
143
144
|
});
|
|
144
145
|
const written = isSave ? await writeDocsFrom(project, config) : void 0;
|
package/dist/index.d.mts
CHANGED
|
@@ -1,6 +1,8 @@
|
|
|
1
1
|
import { AstroCheck, Diagnostic as Diagnostic$1 } from "@astrojs/language-server";
|
|
2
2
|
import * as TypeScript from "typescript";
|
|
3
3
|
//#region src/config.d.ts
|
|
4
|
+
declare const SCANS: readonly ["system", "format", "all"];
|
|
5
|
+
type Scan = (typeof SCANS)[number];
|
|
4
6
|
interface Notice {
|
|
5
7
|
code: string;
|
|
6
8
|
message: string;
|
|
@@ -47,6 +49,7 @@ interface LintDiagnostic extends Diagnostic {
|
|
|
47
49
|
}
|
|
48
50
|
export declare function lint(project: Project, options: {
|
|
49
51
|
componentFolders: string[];
|
|
52
|
+
scan?: Scan;
|
|
50
53
|
files?: string[];
|
|
51
54
|
}): Promise<LintDiagnostic[]>;
|
|
52
55
|
export declare function report(diagnostics: LintDiagnostic[]): string;
|
package/dist/index.mjs
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import { n as writeDocs, t as check } from "./check-
|
|
1
|
+
import { n as report, o as ProjectError, s as openProject, t as lint } from "./lint-Bqu-nKvP.mjs";
|
|
2
|
+
import { n as writeDocs, t as check } from "./check-vsAQPYCD.mjs";
|
|
3
3
|
export { ProjectError, check, lint, openProject, report, writeDocs };
|
|
@@ -6,6 +6,12 @@ import { format } from "node:util";
|
|
|
6
6
|
import { AstroCheck } from "@astrojs/language-server";
|
|
7
7
|
//#region src/config.ts
|
|
8
8
|
const CONFIG_FILE = ".rafters/config.rafters.json";
|
|
9
|
+
const SCANS = [
|
|
10
|
+
"system",
|
|
11
|
+
"format",
|
|
12
|
+
"all"
|
|
13
|
+
];
|
|
14
|
+
const isScan = (value) => SCANS.some((scan) => scan === value);
|
|
9
15
|
function isObject(value) {
|
|
10
16
|
return typeof value === "object" && value !== null && !Array.isArray(value);
|
|
11
17
|
}
|
|
@@ -98,7 +104,10 @@ async function readConfig(root) {
|
|
|
98
104
|
}
|
|
99
105
|
if (intent !== void 0 && typeof intent !== "string") errors.push(fieldError("intent", "expected a string", intent));
|
|
100
106
|
if (veneer !== void 0 && !isObject(veneer)) errors.push(fieldError("veneer", "expected an object", veneer));
|
|
101
|
-
else
|
|
107
|
+
else {
|
|
108
|
+
if (veneer?.mdxPath !== void 0 && typeof veneer.mdxPath !== "string") errors.push(fieldError("veneer.mdxPath", "expected a string", veneer.mdxPath));
|
|
109
|
+
if (veneer?.scan !== void 0 && !isScan(veneer.scan)) errors.push(fieldError("veneer.scan", `expected one of ${SCANS.join(", ")}`, veneer.scan));
|
|
110
|
+
}
|
|
102
111
|
if (errors.length > 0) return {
|
|
103
112
|
ok: false,
|
|
104
113
|
errors
|
|
@@ -107,6 +116,7 @@ async function readConfig(root) {
|
|
|
107
116
|
ok: true,
|
|
108
117
|
intent: typeof intent === "string" ? intent : null,
|
|
109
118
|
componentFolders,
|
|
119
|
+
scan: isObject(veneer) && isScan(veneer.scan) ? veneer.scan : "format",
|
|
110
120
|
notices
|
|
111
121
|
};
|
|
112
122
|
}
|
|
@@ -669,7 +679,15 @@ function ownPropNames(reader, component, sourceFile) {
|
|
|
669
679
|
if (!signature) return [];
|
|
670
680
|
return propsOfSignature(reader, signature, sourceFile, /* @__PURE__ */ new Map()).props.map(({ name }) => name);
|
|
671
681
|
}
|
|
672
|
-
|
|
682
|
+
const INTELLIGENCE_TAGS = [
|
|
683
|
+
"cognitive-load",
|
|
684
|
+
"attention-economics",
|
|
685
|
+
"trust-building",
|
|
686
|
+
"accessibility",
|
|
687
|
+
"semantic-meaning",
|
|
688
|
+
"usage-patterns"
|
|
689
|
+
];
|
|
690
|
+
function placedBlocks(reader, sourceFile) {
|
|
673
691
|
const { ts } = reader;
|
|
674
692
|
const blocks = [];
|
|
675
693
|
const visit = (node) => {
|
|
@@ -684,7 +702,16 @@ function intelligenceBlock(reader, sourceFile) {
|
|
|
684
702
|
block,
|
|
685
703
|
at
|
|
686
704
|
}];
|
|
687
|
-
})
|
|
705
|
+
});
|
|
706
|
+
}
|
|
707
|
+
function intelligenceBlock(reader, sourceFile) {
|
|
708
|
+
return placedBlocks(reader, sourceFile).sort((a, b) => a.at - b.at)[0]?.block;
|
|
709
|
+
}
|
|
710
|
+
function holdsAllIntelligenceTags(reader, sourceFile) {
|
|
711
|
+
return placedBlocks(reader, sourceFile).some(({ block }) => {
|
|
712
|
+
const names = new Set(block.tags?.map((tag) => tag.tagName.text));
|
|
713
|
+
return INTELLIGENCE_TAGS.every((tag) => names.has(tag));
|
|
714
|
+
});
|
|
688
715
|
}
|
|
689
716
|
function intelligence(reader, sourceFile) {
|
|
690
717
|
const { ts } = reader;
|
|
@@ -785,6 +812,13 @@ const CLASS_ATTRIBUTES = /* @__PURE__ */ new Set([
|
|
|
785
812
|
]);
|
|
786
813
|
const MAX_RESOLVE_STEPS = 8;
|
|
787
814
|
const realPath = (file) => existsSync(file) ? realpathSync(file) : file;
|
|
815
|
+
const isClassesFile = (file) => file.endsWith(".classes.ts");
|
|
816
|
+
function qualifies(reader, sourceFile) {
|
|
817
|
+
const { fileName } = sourceFile;
|
|
818
|
+
const stem = fileName.replace(/\.(tsx|astro)$/, "");
|
|
819
|
+
const { sys } = reader.ts;
|
|
820
|
+
return stem !== fileName && sys.fileExists(`${stem}.behavior.ts`) && sys.fileExists(`${stem}.classes.ts`) && holdsAllIntelligenceTags(reader, sourceFile);
|
|
821
|
+
}
|
|
788
822
|
function offsetAt(text, line, column) {
|
|
789
823
|
const breaks = /\r\n|\r|\n/g;
|
|
790
824
|
let start = 0;
|
|
@@ -817,8 +851,8 @@ function elementUsing(ts, node) {
|
|
|
817
851
|
const element = tag.parent;
|
|
818
852
|
return ts.isJsxOpeningLikeElement(element) && element.tagName === tag ? element : void 0;
|
|
819
853
|
}
|
|
820
|
-
function
|
|
821
|
-
let symbol =
|
|
854
|
+
function resolveSymbol({ ts, tc }, start) {
|
|
855
|
+
let symbol = start;
|
|
822
856
|
for (let step = 0; symbol && step < MAX_RESOLVE_STEPS; step += 1) {
|
|
823
857
|
const declaration = symbol.valueDeclaration;
|
|
824
858
|
if (symbol.flags & ts.SymbolFlags.Alias) symbol = tc.getAliasedSymbol(symbol);
|
|
@@ -826,6 +860,10 @@ function resolveTag({ ts, tc }, { tagName }) {
|
|
|
826
860
|
else return symbol;
|
|
827
861
|
}
|
|
828
862
|
}
|
|
863
|
+
function resolveTag(reader, { tagName }) {
|
|
864
|
+
const { ts, tc } = reader;
|
|
865
|
+
return resolveSymbol(reader, tc.getSymbolAtLocation(ts.isPropertyAccessExpression(tagName) ? tagName.name : tagName));
|
|
866
|
+
}
|
|
829
867
|
function deprecatedText({ ts, tc }, element, name) {
|
|
830
868
|
const parameter = tc.getResolvedSignature(element)?.parameters[0];
|
|
831
869
|
const tag = (parameter && tc.getTypeOfSymbol(parameter).getProperty(name))?.getJsDocTags(tc).find((found) => found.name === "deprecated");
|
|
@@ -842,46 +880,82 @@ function utilityOf(className) {
|
|
|
842
880
|
}
|
|
843
881
|
return className.slice(start);
|
|
844
882
|
}
|
|
845
|
-
function
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
883
|
+
function constInitializer(reader, identifier) {
|
|
884
|
+
const { ts, tc } = reader;
|
|
885
|
+
const declaration = resolveSymbol(reader, tc.getSymbolAtLocation(identifier))?.valueDeclaration;
|
|
886
|
+
return declaration && ts.isVariableDeclaration(declaration) && ts.getCombinedNodeFlags(declaration) & ts.NodeFlags.Const ? declaration.initializer : void 0;
|
|
887
|
+
}
|
|
888
|
+
function constString(reader, identifier) {
|
|
889
|
+
const initializer = constInitializer(reader, identifier);
|
|
890
|
+
return initializer && reader.ts.isStringLiteralLike(initializer) ? initializer : void 0;
|
|
891
|
+
}
|
|
892
|
+
function constProperty(reader, { expression, name }) {
|
|
893
|
+
const { ts } = reader;
|
|
894
|
+
let object = ts.isIdentifier(expression) ? constInitializer(reader, expression) : void 0;
|
|
895
|
+
while (object && (ts.isAsExpression(object) || ts.isSatisfiesExpression(object))) object = object.expression;
|
|
896
|
+
if (!object || !ts.isObjectLiteralExpression(object)) return void 0;
|
|
897
|
+
for (const property of object.properties) if (ts.isPropertyAssignment(property) && ts.isIdentifier(property.name) && property.name.text === name.text) return ts.isStringLiteralLike(property.initializer) ? property.initializer : void 0;
|
|
898
|
+
}
|
|
899
|
+
function collectClassStrings(reader, sourceFile, isOwnSource, node, found) {
|
|
900
|
+
const { ts } = reader;
|
|
901
|
+
if (ts.isStringLiteralLike(node)) found.set(node, node);
|
|
902
|
+
else if (ts.isIdentifier(node)) {
|
|
903
|
+
const literal = constString(reader, node);
|
|
904
|
+
const declaredIn = literal?.getSourceFile();
|
|
905
|
+
if (!literal || !declaredIn) return;
|
|
906
|
+
if (declaredIn === sourceFile) found.set(literal, literal);
|
|
907
|
+
else if (isOwnSource(declaredIn.fileName)) found.set(node, literal);
|
|
908
|
+
} else {
|
|
909
|
+
const literal = ts.isPropertyAccessExpression(node) ? constProperty(reader, node) : void 0;
|
|
910
|
+
const declaredIn = literal?.getSourceFile();
|
|
911
|
+
if (literal && declaredIn && (declaredIn === sourceFile || isOwnSource(declaredIn.fileName))) {
|
|
912
|
+
found.set(node, literal);
|
|
913
|
+
return;
|
|
914
|
+
}
|
|
915
|
+
ts.forEachChild(node, (child) => {
|
|
916
|
+
collectClassStrings(reader, sourceFile, isOwnSource, child, found);
|
|
917
|
+
});
|
|
918
|
+
}
|
|
851
919
|
}
|
|
852
|
-
function bracketsInLiteral(reader, file, sourceFile, literal) {
|
|
853
|
-
return [...literal.getText(
|
|
920
|
+
function bracketsInLiteral(reader, file, sourceFile, at, literal) {
|
|
921
|
+
return [...literal.getText(literal.getSourceFile()).slice(1, -1).matchAll(/\S+/g)].flatMap((match) => {
|
|
854
922
|
const [className] = match;
|
|
855
923
|
if (!utilityOf(className).includes("[")) return [];
|
|
856
|
-
const
|
|
924
|
+
const inLiteral = at === literal;
|
|
925
|
+
const position = positionOf(reader, sourceFile, at.getStart(sourceFile) + (inLiteral ? 1 + match.index : 0));
|
|
857
926
|
if (!position) return [];
|
|
858
927
|
return [{
|
|
859
928
|
file,
|
|
860
929
|
line: position.line,
|
|
861
930
|
column: position.column,
|
|
862
931
|
endLine: position.line,
|
|
863
|
-
endColumn: position.column + className.length,
|
|
932
|
+
endColumn: position.column + (inLiteral ? className.length : at.getWidth(sourceFile)),
|
|
864
933
|
severity: "error",
|
|
865
934
|
code: "rafters(arbitrary-value)",
|
|
866
935
|
message: `${className} is an arbitrary value`
|
|
867
936
|
}];
|
|
868
937
|
});
|
|
869
938
|
}
|
|
870
|
-
function bracketValues(reader, file, sourceFile) {
|
|
939
|
+
function bracketValues(reader, file, sourceFile, isOwnSource) {
|
|
871
940
|
const { ts } = reader;
|
|
872
|
-
const
|
|
941
|
+
const strings = /* @__PURE__ */ new Map();
|
|
942
|
+
const everyLiteral = isClassesFile(sourceFile.fileName);
|
|
943
|
+
if (everyLiteral && !isOwnSource(sourceFile.fileName)) return [];
|
|
873
944
|
const visit = (node) => {
|
|
874
|
-
if (
|
|
945
|
+
if (everyLiteral) {
|
|
946
|
+
if (ts.isStringLiteralLike(node)) strings.set(node, node);
|
|
947
|
+
} else if (ts.isJsxAttribute(node) && node.initializer) {
|
|
875
948
|
const { name } = node;
|
|
876
949
|
const attribute = ts.isJsxNamespacedName(name) ? `${name.namespace.text}:${name.name.text}` : name.text;
|
|
877
|
-
if (CLASS_ATTRIBUTES.has(attribute))
|
|
878
|
-
}
|
|
950
|
+
if (CLASS_ATTRIBUTES.has(attribute)) collectClassStrings(reader, sourceFile, isOwnSource, node.initializer, strings);
|
|
951
|
+
} else if (ts.isCallExpression(node) && ts.isIdentifier(node.expression) && node.expression.text === "classy") for (const argument of node.arguments) collectClassStrings(reader, sourceFile, isOwnSource, argument, strings);
|
|
879
952
|
ts.forEachChild(node, visit);
|
|
880
953
|
};
|
|
881
954
|
visit(sourceFile);
|
|
882
|
-
return
|
|
955
|
+
return [...strings].flatMap(([at, literal]) => bracketsInLiteral(reader, file, sourceFile, at, literal));
|
|
883
956
|
}
|
|
884
957
|
const FILL_WORD = /^[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
958
|
+
const FILL_TOKEN = /^bg-fill-[a-z][a-z0-9]*(-[a-z0-9]+)*$/;
|
|
885
959
|
const FILL_ALPHA = /^(100|[1-9]?[0-9])$/;
|
|
886
960
|
const AUTHORED_FILL = "a fill the designer authors, fill=\"bg-fill-<name>\"";
|
|
887
961
|
const isFillStop = (stop) => {
|
|
@@ -901,24 +975,24 @@ async function loadSurfaces(root) {
|
|
|
901
975
|
return { failure: error instanceof Error ? error.message : String(error) };
|
|
902
976
|
}
|
|
903
977
|
}
|
|
904
|
-
function
|
|
978
|
+
function valueLiteral(ts, node) {
|
|
905
979
|
const { initializer } = node;
|
|
906
980
|
if (!initializer) return void 0;
|
|
907
981
|
if (ts.isStringLiteral(initializer)) return initializer;
|
|
908
982
|
const inner = ts.isJsxExpression(initializer) ? initializer.expression : void 0;
|
|
909
983
|
return inner && ts.isStringLiteralLike(inner) ? inner : void 0;
|
|
910
984
|
}
|
|
911
|
-
function
|
|
985
|
+
function valueAttributes({ ts }, sourceFile) {
|
|
912
986
|
const found = [];
|
|
913
987
|
const visit = (node) => {
|
|
914
|
-
if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && node.name.text === "fill") found.push(node);
|
|
988
|
+
if (ts.isJsxAttribute(node) && ts.isIdentifier(node.name) && (node.name.text === "fill" || node.name.text === "surface")) found.push(node);
|
|
915
989
|
ts.forEachChild(node, visit);
|
|
916
990
|
};
|
|
917
991
|
visit(sourceFile);
|
|
918
992
|
return found;
|
|
919
993
|
}
|
|
920
994
|
async function lint(project, options) {
|
|
921
|
-
const files = options.files ?? project.checker.getRootFileNames().filter((file) => file.endsWith(".tsx") || file.endsWith(".astro")).map((file) => relativeFile(project.root, file));
|
|
995
|
+
const files = options.files?.filter((file) => !file.endsWith(".ts") || isClassesFile(file)) ?? project.checker.getRootFileNames().filter((file) => file.endsWith(".tsx") || file.endsWith(".astro") || isClassesFile(file)).map((file) => relativeFile(project.root, file));
|
|
922
996
|
const perFile = [];
|
|
923
997
|
for (const file of files) perFile.push([file, await project.diagnostics(file)]);
|
|
924
998
|
const program = project.program();
|
|
@@ -933,6 +1007,17 @@ async function lint(project, options) {
|
|
|
933
1007
|
const real = realPath(file);
|
|
934
1008
|
return folders.some((folder) => real.startsWith(folder));
|
|
935
1009
|
};
|
|
1010
|
+
const scan = options.scan ?? "format";
|
|
1011
|
+
const projectRoot = realPath(project.root);
|
|
1012
|
+
const isOwnSource = (file) => {
|
|
1013
|
+
const [first, ...rest] = path.relative(projectRoot, realPath(file)).split(path.sep);
|
|
1014
|
+
return first !== ".." && first !== "node_modules" && !rest.includes("node_modules");
|
|
1015
|
+
};
|
|
1016
|
+
const isExempt = (sourceFile) => isUnderFolder(sourceFile.fileName) || qualifies(reader, sourceFile);
|
|
1017
|
+
const isCheckedUse = (declaredIn) => {
|
|
1018
|
+
if (scan === "system") return isUnderFolder(declaredIn.fileName);
|
|
1019
|
+
return isExempt(declaredIn) || scan === "all" && isOwnSource(declaredIn.fileName);
|
|
1020
|
+
};
|
|
936
1021
|
const propsHelp = /* @__PURE__ */ new Map();
|
|
937
1022
|
const reportedUse = (sourceFile, diagnostic) => {
|
|
938
1023
|
const offset = programOffset(reader, sourceFile, diagnostic);
|
|
@@ -943,7 +1028,7 @@ async function lint(project, options) {
|
|
|
943
1028
|
const declaration = component?.valueDeclaration ?? component?.declarations?.[0];
|
|
944
1029
|
if (!element || !component || !declaration) return void 0;
|
|
945
1030
|
const declaredIn = declaration.getSourceFile();
|
|
946
|
-
if (!
|
|
1031
|
+
if (!isCheckedUse(declaredIn)) return void 0;
|
|
947
1032
|
let help;
|
|
948
1033
|
if (diagnostic.code === "ts(6385)") help = deprecatedText(reader, element, node.getText(sourceFile));
|
|
949
1034
|
else {
|
|
@@ -966,15 +1051,44 @@ async function lint(project, options) {
|
|
|
966
1051
|
if ("failure" in loaded) return `the project's tokens could not be loaded, so no replacement is named: ${loaded.failure}`;
|
|
967
1052
|
return loaded.words.has(value) ? `surface="${value}"` : AUTHORED_FILL;
|
|
968
1053
|
};
|
|
969
|
-
const
|
|
1054
|
+
const fillFinding = async (value) => {
|
|
1055
|
+
if (value === "" || FILL_TOKEN.test(value)) return void 0;
|
|
1056
|
+
const form = retiredFillForm(value);
|
|
1057
|
+
if (!form) return {
|
|
1058
|
+
code: "rafters(invalid-fill)",
|
|
1059
|
+
message: `fill="${value}" is not exactly one bg-fill-* token`
|
|
1060
|
+
};
|
|
1061
|
+
return {
|
|
1062
|
+
code: "rafters(retired-fill)",
|
|
1063
|
+
message: `fill="${value}" is a retired fill value`,
|
|
1064
|
+
help: await replacement(value, form)
|
|
1065
|
+
};
|
|
1066
|
+
};
|
|
1067
|
+
const surfaceFinding = async (value) => {
|
|
1068
|
+
if (value === "") return void 0;
|
|
1069
|
+
surfaces ??= loadSurfaces(project.root);
|
|
1070
|
+
const loaded = await surfaces;
|
|
1071
|
+
if ("failure" in loaded) return {
|
|
1072
|
+
code: "rafters(unknown-surface)",
|
|
1073
|
+
message: `surface="${value}" could not be checked`,
|
|
1074
|
+
help: `the project's tokens could not be loaded, so no surface is named: ${loaded.failure}`
|
|
1075
|
+
};
|
|
1076
|
+
if (loaded.words.has(value)) return void 0;
|
|
1077
|
+
return {
|
|
1078
|
+
code: "rafters(unknown-surface)",
|
|
1079
|
+
message: `surface="${value}" is not a surface in the project's tokens`
|
|
1080
|
+
};
|
|
1081
|
+
};
|
|
1082
|
+
const badValues = async (file, sourceFile) => {
|
|
970
1083
|
const flagged = [];
|
|
971
|
-
for (const attribute of
|
|
972
|
-
const literal =
|
|
973
|
-
|
|
974
|
-
if (!literal || !form) continue;
|
|
1084
|
+
for (const attribute of valueAttributes(reader, sourceFile)) {
|
|
1085
|
+
const literal = valueLiteral(ts, attribute);
|
|
1086
|
+
if (!literal) continue;
|
|
975
1087
|
const component = resolveTag(reader, attribute.parent.parent);
|
|
976
1088
|
const declaration = component?.valueDeclaration ?? component?.declarations?.[0];
|
|
977
|
-
if (!declaration || !
|
|
1089
|
+
if (!declaration || !isCheckedUse(declaration.getSourceFile())) continue;
|
|
1090
|
+
const finding = await (attribute.name.getText(sourceFile) === "fill" ? fillFinding(literal.text) : surfaceFinding(literal.text));
|
|
1091
|
+
if (!finding) continue;
|
|
978
1092
|
const start = positionOf(reader, sourceFile, attribute.getStart(sourceFile));
|
|
979
1093
|
const end = positionOf(reader, sourceFile, attribute.end);
|
|
980
1094
|
if (!start || !end) continue;
|
|
@@ -985,9 +1099,7 @@ async function lint(project, options) {
|
|
|
985
1099
|
endLine: end.line,
|
|
986
1100
|
endColumn: end.column,
|
|
987
1101
|
severity: "error",
|
|
988
|
-
|
|
989
|
-
message: `fill="${literal.text}" is a retired fill value`,
|
|
990
|
-
help: await replacement(literal.text, form)
|
|
1102
|
+
...finding
|
|
991
1103
|
});
|
|
992
1104
|
}
|
|
993
1105
|
return flagged;
|
|
@@ -1001,9 +1113,9 @@ async function lint(project, options) {
|
|
|
1001
1113
|
const reported = reportedUse(sourceFile, diagnostic);
|
|
1002
1114
|
if (reported) found.push(reported);
|
|
1003
1115
|
}
|
|
1004
|
-
if (!
|
|
1005
|
-
found.push(...bracketValues(reader, file, sourceFile));
|
|
1006
|
-
found.push(...await
|
|
1116
|
+
if (!isExempt(sourceFile)) {
|
|
1117
|
+
found.push(...bracketValues(reader, file, sourceFile, isOwnSource));
|
|
1118
|
+
found.push(...await badValues(file, sourceFile));
|
|
1007
1119
|
}
|
|
1008
1120
|
}
|
|
1009
1121
|
return found.sort((a, b) => compareCodePoints(a.file, b.file) || a.line - b.line || a.column - b.column);
|
|
@@ -1021,4 +1133,4 @@ function report(diagnostics) {
|
|
|
1021
1133
|
return lines.map((line) => `${line}\n`).join("");
|
|
1022
1134
|
}
|
|
1023
1135
|
//#endregion
|
|
1024
|
-
export {
|
|
1136
|
+
export { resolveComponents as a, readConfig as c, relativeFile as i, report as n, ProjectError as o, compareCodePoints as r, openProject as s, lint as t };
|
package/dist/worker.mjs
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { c as readConfig, s as openProject, t as lint } from "./lint-Bqu-nKvP.mjs";
|
|
2
2
|
import path from "node:path";
|
|
3
3
|
import { existsSync } from "node:fs";
|
|
4
4
|
import { promisify } from "node:util";
|
|
@@ -30,7 +30,7 @@ process.stdout.write(JSON.stringify(await check(${JSON.stringify(root)}, { save:
|
|
|
30
30
|
async function serve(root) {
|
|
31
31
|
const config = await readConfig(root);
|
|
32
32
|
if (!config.ok) throw new Error(messages(config.errors));
|
|
33
|
-
const { componentFolders } = config;
|
|
33
|
+
const { componentFolders, scan } = config;
|
|
34
34
|
let project = await openProject(root);
|
|
35
35
|
const unsaved = /* @__PURE__ */ new Map();
|
|
36
36
|
const outside = /* @__PURE__ */ new Set();
|
|
@@ -57,6 +57,7 @@ async function serve(root) {
|
|
|
57
57
|
}
|
|
58
58
|
return lint(project, {
|
|
59
59
|
componentFolders,
|
|
60
|
+
scan,
|
|
60
61
|
files: [file]
|
|
61
62
|
});
|
|
62
63
|
};
|