このブログを作るために Deno + lume で Panda CSS を使おうと思ったら詰まったのでメモ。
https://github.com/chakra-ui/panda/discussions/3148 でも言及されているように、 Deno で Panda CSS を利用する場合、通常の設定では型定義が反映されず、 Panda CSS の恩恵が受けにくい。
Panda CSS は styled-system に .mjs と、その型定義の .d.mts ファイルを生成する。
その際に tsc では以下のように自動で型定義ファイルのルックアップを行うため、追加の記述なしに型定義が利用できる。
TypeScript always wants to resolve internally to a file that can provide type information, while ensuring that the runtime or bundler can use the same path to resolve to a file that provides a JavaScript implementation. For any module specifier that would, according to the moduleResolution algorithm specified, trigger a lookup of a JavaScript file in the runtime or bundler, TypeScript will first try to find a TypeScript implementation file or type declaration file with the same name and analagous file extension. https://www.typescriptlang.org/docs/handbook/modules/reference.html#file-extension-substitution
一方、 Deno ではそのようなルックアップが行われないため、.mjs ファイル内で明示的に .d.mts を指定する必要がある。
tsc will pick up d.ts files that are siblings of a js file and have the same basename, automatically. Deno does not do this. You must explicitly specify either in the .js file (the source), or the .ts file (the importer) where to find the .d.ts file. https://docs.deno.com/runtime/fundamentals/typescript/#providing-declaration-files
_config.ts
site.use(postcss({ plugins: [pandacss] }));
site.add("global.css");
src/panda-css-helper.ts
import { walk } from "@std/fs";
import { basename } from "@std/path";
import pandaConfig from "../panda.config.mjs";
const ext = pandaConfig.outExtension ?? "mjs";
const srcExt = `.${ext}`;
const declExt = `.d.${ext.replace("j", "t")}`;
const srcDir = pandaConfig.outdir ?? "styled-system";
let count = 0;
for await (const entry of walk(srcDir, {
exts: [srcExt],
includeDirs: false,
})) {
const filePath = entry.path;
const declPath = filePath.replace(new RegExp(`${srcExt}$`), declExt);
try {
await Deno.stat(declPath);
} catch {
continue;
}
const fileName = basename(filePath);
const directive = `// @ts-self-types="./${fileName.replace(
new RegExp(`${srcExt}$`),
declExt,
)}"\n`;
const original = await Deno.readTextFile(filePath);
if (!original.startsWith("// @ts-self-types")) {
await Deno.writeTextFile(filePath, directive + original);
count += 1;
}
}
console.log(`✔️ added \`@ts-self-types\` directive to ${count} files`);
deno.json
{
...
"tasks": {
+ "prepare": "deno run -A --node-modules-dir npm:@pandacss/dev codegen && deno run -A src/panda-css-helper.ts",
}
...
}
あとは deno task prepare を実行するだけ。