release: WRNexusJS 0.8.0
This commit is contained in:
1 parent
87507edf59
commit
586a6db8ff
625 files changed
+243608
-11210
No files matched your search
@@ -0,0 +1,5 @@
|
||||
# @wrnexus/typecheck
|
||||
|
||||
Static type checking for `.wrn` declarations, props, state, outputs, functions, stores, and generated virtual TypeScript files.
|
||||
|
||||
The package is compiler tooling rather than a browser UI package, so its public kit consists of programmatic typecheck helpers and diagnostics.
|
||||
@@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "@wrnexus/typecheck",
|
||||
"version": "0.7.0",
|
||||
"version": "0.8.0",
|
||||
"type": "module",
|
||||
"main": "src/index.ts",
|
||||
"exports": {
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
import { existsSync, readFileSync } from "node:fs";
|
||||
import { existsSync, readFileSync, readdirSync } from "node:fs";
|
||||
import { dirname, join, normalize, resolve } from "node:path";
|
||||
import ts from "typescript";
|
||||
import {
|
||||
@@ -51,6 +51,39 @@ function resolveImportedWrn(source: string, filePath: string, appRoot: string):
|
||||
return null;
|
||||
}
|
||||
|
||||
function workspacePackagePaths(appRoot: string): Record<string, string[]> {
|
||||
let current = resolve(appRoot);
|
||||
while (true) {
|
||||
const manifestPath = join(current, "package.json");
|
||||
if (existsSync(manifestPath)) {
|
||||
try {
|
||||
const manifest = JSON.parse(readFileSync(manifestPath, "utf8"));
|
||||
if (Array.isArray(manifest.workspaces) && existsSync(join(current, "packages"))) {
|
||||
const paths: Record<string, string[]> = {};
|
||||
for (const entry of readdirSync(join(current, "packages"), { withFileTypes: true })) {
|
||||
if (!entry.isDirectory()) continue;
|
||||
const packageRoot = join(current, "packages", entry.name);
|
||||
const packageManifestPath = join(packageRoot, "package.json");
|
||||
if (!existsSync(packageManifestPath)) continue;
|
||||
const packageManifest = JSON.parse(readFileSync(packageManifestPath, "utf8"));
|
||||
if (typeof packageManifest.name !== "string") continue;
|
||||
const main =
|
||||
typeof packageManifest.main === "string" ? packageManifest.main : "src/index.ts";
|
||||
paths[packageManifest.name] = [join(packageRoot, main)];
|
||||
paths[`${packageManifest.name}/*`] = [join(packageRoot, "src", "*.ts")];
|
||||
}
|
||||
return paths;
|
||||
}
|
||||
} catch {
|
||||
/* continue toward a parent workspace */
|
||||
}
|
||||
}
|
||||
const parent = dirname(current);
|
||||
if (parent === current) return {};
|
||||
current = parent;
|
||||
}
|
||||
}
|
||||
|
||||
function importedWrnDeclarations(ast: PageAst, filePath: string, appRoot: string): string {
|
||||
const declarations: string[] = [];
|
||||
for (const entry of ast.structuredImports) {
|
||||
@@ -106,7 +139,7 @@ function functionDeclaration(fn: RuntimeFunctionDecl): string {
|
||||
const params = fn.parameters
|
||||
.map(
|
||||
(param) =>
|
||||
`${param.name}${param.optional ? "?" : ""}${param.valueType ? `: ${param.valueType}` : ": unknown"}${param.default ? ` = ${param.default}` : ""}`,
|
||||
`${param.name}${param.optional ? "?" : ""}${param.valueType ? `: ${param.valueType}` : param.name === "event" ? ": CustomEvent<any> & { target: HTMLElement }" : ": unknown"}${param.default ? ` = ${param.default}` : ""}`,
|
||||
)
|
||||
.join(", ");
|
||||
return `export ${fn.async ? "async " : ""}function ${fn.name}(${params})${fn.returnType ? `: ${fn.returnType}` : ""} {${fn.body}}`;
|
||||
@@ -186,6 +219,12 @@ export function virtualTypeScriptModule(
|
||||
)
|
||||
.join("; ");
|
||||
append(`declare const output: { ${outputType} };`);
|
||||
for (const output of ast.outputs)
|
||||
append(
|
||||
`declare const ${output.name}: (${output.payload ? `${output.payload.name}${output.payload.optional ? "?" : ""}: ${output.payload.valueType}` : ""}) => void;`,
|
||||
);
|
||||
for (const match of source.matchAll(/@event\s+([A-Za-z_$][\w$]*)\s*=\s*function/g))
|
||||
append(`declare const ${match[1]}: ((...args: unknown[]) => void) | undefined;`);
|
||||
append(`declare const server: { ${serverType} };`);
|
||||
append(`declare const props: Readonly<${ast.name}Props>;`);
|
||||
append("declare const refs: Record<string, Element | null>;");
|
||||
@@ -601,8 +640,8 @@ export function checkWrnSource(
|
||||
allowArbitraryExtensions: true,
|
||||
noEmit: true,
|
||||
baseUrl: appRoot,
|
||||
paths: { "@/*": ["app/*"] },
|
||||
lib: ["lib.esnext.d.ts", "lib.dom.d.ts"],
|
||||
paths: { "@/*": ["app/*"], ...workspacePackagePaths(appRoot) },
|
||||
lib: ["lib.esnext.d.ts", "lib.dom.d.ts", "lib.dom.iterable.d.ts"],
|
||||
};
|
||||
const rootNames = [virtual.fileName, ...appTypes.files.keys()];
|
||||
const program = ts.createProgram(
|
||||
|
||||
@@ -45,7 +45,7 @@ page Home {
|
||||
expect(diagnostics.some((item) => item.code === "WRN-COMPONENT-UNKNOWN-PROP")).toBe(true);
|
||||
expect(diagnostics.some((item) => item.code === "WRN-COMPONENT-PROP-LITERAL")).toBe(true);
|
||||
expect(diagnostics.some((item) => item.code === "WRN-OUTPUT-UNKNOWN-HANDLER")).toBe(true);
|
||||
});
|
||||
}, 15_000);
|
||||
|
||||
test("types imported store actions", () => {
|
||||
const root = fixture();
|
||||
@@ -59,4 +59,4 @@ page Home {
|
||||
{ appRoot: root, filePath },
|
||||
);
|
||||
expect(diagnostics.some((item) => item.code === "WRN-TYPE-2345")).toBe(true);
|
||||
});
|
||||
}, 15_000);
|
||||
Reference in new issue
Block a user