Skip to content

Commit

Permalink
use ts-blank-space to strip types
Browse files Browse the repository at this point in the history
  • Loading branch information
AdrianGonz97 committed Oct 27, 2024
1 parent ddd7521 commit 17688ee
Show file tree
Hide file tree
Showing 5 changed files with 131 additions and 67 deletions.
27 changes: 19 additions & 8 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions sites/docs/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@
"tailwind-variants": "^0.2.1",
"tailwindcss": "^3.4.4",
"tailwindcss-animate": "^1.0.7",
"ts-blank-space": "^0.4.1",
"tslib": "^2.6.3",
"tsx": "^4.16.2",
"typescript": "~5.5.3",
Expand Down
21 changes: 17 additions & 4 deletions sites/docs/scripts/build-registry.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,19 @@ import { buildRegistry } from "./registry";
import { BASE_STYLES, BASE_STYLES_WITH_VARIABLES, THEME_STYLES_WITH_VARIABLES } from "./templates";
import { getChunks } from "./transform-chunks";
import { transformContent } from "./transformers";
import prettier from "prettier";
import prettierPluginSvelte from "prettier-plugin-svelte";

const REGISTRY_PATH = path.resolve("static", "registry");
const REGISTRY_IGNORE = ["super-form"];

const prettierConfig: prettier.Config = {
useTabs: true,
singleQuote: false,
trailingComma: "es5",
printWidth: 100,
};

function writeFileWithDirs(
filePath: string,
data: string,
Expand Down Expand Up @@ -178,11 +187,15 @@ export const Index = {

const jsFiles = await Promise.all(
files.map(async (file) => {
const content = (await transformContent(file.content, file.name)).replaceAll(
" ",
"\t"
);
let content = await transformContent(file.content, file.name);
const fileName = file.name.replace(".ts", ".js");
// format
content = await prettier.format(content, {
...prettierConfig,
filepath: fileName,
plugins: [prettierPluginSvelte],
overrides: [{ files: "*.svelte", options: { parser: "svelte" } }],
});
return {
name: fileName,
content,
Expand Down
146 changes: 92 additions & 54 deletions sites/docs/scripts/transformers.ts
Original file line number Diff line number Diff line change
@@ -1,79 +1,117 @@
import { vitePreprocess } from "@sveltejs/vite-plugin-svelte";
import { preprocess } from "svelte/compiler";
import ts from "typescript";
import { walk } from "estree-walker";
import { preprocess, parse, type PreprocessorGroup } from "svelte/compiler";
import tsBlankSpace from "ts-blank-space";
import MagicString from "magic-string";

export type TransformOpts = {
filename: string;
content: string;
// baseColor?: z.infer<typeof registryBaseColorSchema>; - will use later
};

// const sharedPrettierConfig = {
// useTabs: true,
// tabWidth: 4,
// singleQuote: false,
// trailingComma: "es5" as const,
// printWidth: 100,
// endOfLine: "lf" as const,
// bracketSameLine: false,
// };

// const registrySveltePrettierConfig = {
// ...sharedPrettierConfig,
// pluginSearchDirs: ["./node_modules/prettier-plugin-svelte"],
// parser: "svelte",
// svelteStrictMode: false,
// plugins: ["prettier-plugin-svelte"],
// };

// const registryJSPrettierConfig = {
// ...sharedPrettierConfig,
// parser: "babel",
// };

export async function transformContent(content: string, filename: string) {
if (filename.endsWith(".svelte")) {
return transformSvelteTStoJS(content, filename);
} else {
return transformTStoJS(content, filename);
}
}

async function transformSvelteTStoJS(content: string, filename: string) {
try {
const { code } = await preprocess(content, [vitePreprocess()]);
let s = code.replaceAll(/<script lang=['"]ts['"]>/g, "<script>");
s = s.replaceAll(/void 0/g, "undefined");
return s;
const { code } = await preprocess(content, [stripMarkupTypes(), stripScriptTypes()], {
filename,
});
return code;
} catch (e) {
throw new Error(`Error preprocessing Svelte file: ${filename} \n ${e}`);
}
}

const compilerOptions: ts.CompilerOptions = {
target: ts.ScriptTarget.ESNext,
module: ts.ModuleKind.ESNext,
isolatedModules: true,
preserveValueImports: true,
lib: ["esnext", "DOM", "DOM.Iterable"],
moduleResolution: ts.ModuleResolutionKind.Bundler,
esModuleInterop: true,
ignoreDeprecations: "5.0",
};

function transformTStoJS(content: string, filename: string) {
const { outputText, diagnostics } = ts.transpileModule(content, {
compilerOptions,
reportDiagnostics: true,
});

// Check for compilation errors
if (diagnostics && diagnostics.length > 0) {
// Throw the errors so the user can see them/create an issue about them.
const output = tsBlankSpace(content, (node) => {
throw new Error(
`Error compiling TypeScript to JavaScript for file: ${filename} \n ${diagnostics}`
`Error compiling TypeScript to JavaScript for file: ${filename} \n ${node.pos}`
);
} else {
return outputText;
}
});

return output;
}

function stripScriptTypes(): PreprocessorGroup {
return {
// strip the `lang="ts"` attribute
script: ({ content, attributes, filename }) => {
if (attributes["lang"] !== "ts") return;
delete attributes["lang"];
return { code: transformTStoJS(content, filename!), attributes };
},
};
}

function stripMarkupTypes(): PreprocessorGroup {
return {
markup: ({ content, filename }) => {
const ms = new MagicString(content);
const ast = parse(content, { filename, modern: true });

// @ts-expect-error simmer down
walk(ast.fragment, {
enter(node: (typeof ast)["fragment"]["nodes"][number]) {
// ignore typescript specific nodes
if (node.type.startsWith("TS")) return;

if (node.type === "SnippetBlock") {
if (node.parameters.length === 0) return;
// @ts-expect-error relax buddy
const start = node.parameters.at(0)!.start;
const end =
// @ts-expect-error you too
node.parameters.at(-1)!.typeAnnotation?.end ??
// @ts-expect-error and you
node.parameters.at(-1)!.end;

const params = content.substring(start, end);
// temporarily wraps the params in an arrow function so that it's parsable
const arrow = " => {}";
const wrapped = `(${params})${arrow}`;
const stripped = transformTStoJS(wrapped, filename!)
.replace(arrow, "")
.slice(1, -1);
ms.update(start, end, stripped);
} else if (node.type === "ConstTag") {
// @ts-expect-error hush
const { start, end } = node.declaration;
const expression = content.substring(start, end);
const wrapped = `(${expression})`;
const stripped = transformTStoJS(wrapped, filename!).slice(1, -1);
ms.update(start, end, stripped);

this.skip();
} else if (node.type === "RenderTag" || node.type === "ExpressionTag") {
// @ts-expect-error take a breather
const { start, end } = node.expression;
const expression = content.substring(start, end);
const wrapped = `(${expression})`;
const stripped = transformTStoJS(wrapped, filename!).slice(1, -1);
ms.update(start, end, stripped);

this.skip();
} else if ("expression" in node) {
// @ts-expect-error trust me
const { start, end } = node.expression;

const expression = content.substring(start, end);
const wrapped = `(${expression})`;

// removes the `()`
const stripped = transformTStoJS(wrapped, filename!).slice(1, -1);

ms.update(start, end, stripped);
}
},
});

return { code: ms.toString() };
},
};
}
3 changes: 2 additions & 1 deletion sites/docs/tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,8 @@
"strict": true,
"moduleResolution": "NodeNext",
"module": "NodeNext",
"verbatimModuleSyntax": true
"verbatimModuleSyntax": true,
"useDefineForClassFields": true
},
"include": [
// SvelteKit defaults
Expand Down

0 comments on commit 17688ee

Please sign in to comment.