Skip to content

Commit

Permalink
resolve tailwidn deps (#234)
Browse files Browse the repository at this point in the history
  • Loading branch information
tlgimenes authored May 1, 2024
1 parent e9bfa8c commit ba608d4
Show file tree
Hide file tree
Showing 3 changed files with 177 additions and 15 deletions.
4 changes: 2 additions & 2 deletions import_map.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"imports": {
"deco-sites/std/": "./",
"deco/": "https://denopkg.com/deco-cx/deco@1.57.18/",
"deco/": "https://denopkg.com/deco-cx/deco@1.62.2/",
"partytown/": "https://deno.land/x/[email protected]/",
"$fresh/": "https://deno.land/x/[email protected]/",
"preact": "https://esm.sh/[email protected]",
Expand All @@ -11,4 +11,4 @@
"@preact/signals-core": "https://esm.sh/@preact/[email protected]",
"std/": "https://deno.land/[email protected]/"
}
}
}
146 changes: 146 additions & 0 deletions plugins/tailwind/deno.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,146 @@
// deno-lint-ignore-file no-explicit-any
import {
ImportMapBuilder,
ImportMapResolver,
} from "deco/engine/importmap/builder.ts";
import { initLoader, parsePath } from "deco/engine/schema/parser.ts";
import { join, toFileUrl } from "std/path/mod.ts";

const visit = (program: any, visitor: Record<string, (node: any) => void>) => {
for (const value of Object.values(program)) {
const nodeType = (value as any)?.type;

if (nodeType in visitor) {
visitor[nodeType](value);
}

if (value && typeof value === "object") {
visit(value, visitor);
}
}
};

const importsFrom = async (path: string): Promise<string[]> => {
const program = await parsePath(path);

if (!program) {
return [];
}

const imports = new Set<string>();

visit(program, {
// Resovles static "import from" statements
ImportDeclaration: (node: any) => {
const specifier = node.source.value;

if (typeof specifier === "string") {
imports.add(specifier);
}
},
// Resolves dynamic "import()" statements
CallExpression: (node: any) => {
if (node.callee?.type !== "Import") {
return;
}

const arg0 = node.arguments?.[0]?.expression;
if (arg0.type !== "StringLiteral") {
console.warn([
`Invalid import statement`,
`TailwindCSS will not load classes from dependencies of ${path}`,
"To fix this issue, make sure you are following the patterns:",
` Statically evaluated imports WORK`,
` import("path/to/file")`,
` lazy(() => import("path/to/file"))`,
` Dinamically evaluated imports FAIL`,
` import(\`path/to/file\`)`,
` lazy((variable) => import(\`path/to/file/\${variable}\`))`,
"",
].join("\n"));

return;
}

imports.add(arg0.value);
},
});

return [...imports.values()];
};

const resolveRecursively = async (
path: string,
context: string,
loader: (specifier: string) => Promise<string | undefined>,
importMapResolver: ImportMapResolver,
cache: Map<string, string>,
) => {
const resolvedPath = importMapResolver.resolve(path, context);

if (!resolvedPath?.endsWith(".tsx") || cache.has(resolvedPath)) {
return;
}

const [content, imports] = await Promise.all([
loader(resolvedPath),
importsFrom(resolvedPath),
]);

if (!content) {
return;
}

cache.set(resolvedPath, content);

await Promise.all(imports.map((imp) =>
resolveRecursively(
imp,
resolvedPath,
loader,
importMapResolver,
cache,
)
));
};

const readImportMap = async () => {
const [import_map, deno_json] = await Promise.all([
Deno.readTextFile("./import_map.json").then(JSON.parse).catch(() => null),
Deno.readTextFile("./deno.json").then(JSON.parse).catch(() => null),
]);

return {
imports: {
...import_map?.imports,
...deno_json?.imports,
},
scopes: {
...import_map?.scopes,
...deno_json?.scopes,
},
};
};

export const resolveDeps = async (
entries: string[],
cache: Map<string, string>,
) => {
const importMap = await readImportMap();
const loader = initLoader();

const importMapResolver = ImportMapBuilder.new().mergeWith(
importMap,
toFileUrl(join(Deno.cwd(), "/")).href,
);

for (const entry of entries) {
await resolveRecursively(
entry,
Deno.cwd(),
loader,
importMapResolver,
cache,
);
}
};
42 changes: 29 additions & 13 deletions plugins/tailwind/mod.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
import type { Plugin } from "$fresh/server.ts";
import { resolveDeps } from "./deno.ts";
import { Context } from "deco/deco.ts";
import { join } from "std/path/mod.ts";
import { bundle, Config, loadTailwindConfig } from "./bundler.ts";
import { VFS } from "deco/runtime/fs/mod.ts";

import { walk } from "std/fs/walk.ts";
import { join, toFileUrl } from "std/path/mod.ts";
import { bundle, Config, loadTailwindConfig } from "./bundler.ts";
export type { Config } from "./bundler.ts";

const root: string = Deno.cwd();
Expand Down Expand Up @@ -100,23 +101,38 @@ export const plugin = (config?: Config): Plugin => {
const mode = fresh.dev ? "dev" : "prod";
const ctx = Context.active();

const withReleaseContent = (config: Config) => {
const withReleaseContent = async (config: Config) => {
const ctx = Context.active();
const allTsxFiles = new Map<string, string>();

const vfs = ctx.fs;
if (!vfs || !(vfs instanceof VFS)) {
return config;
}
// init search graph with local FS
const roots = new Set<string>();

for await (
const entry of walk(Deno.cwd(), {
includeDirs: false,
includeFiles: true,
exts: [".tsx"],
})
) {
roots.add(toFileUrl(entry.path).href);
}

const allTsxFiles = [];
for (const [path, file] of Object.entries(vfs.fileSystem)) {
if (path.endsWith(".tsx") && file.content) {
allTsxFiles.push(file.content);
await resolveDeps([...roots.values()], allTsxFiles);
} else {
// init search graph with virtual FS
for (const [path, file] of Object.entries(vfs.fileSystem)) {
if (path.endsWith(".tsx") && file.content) {
allTsxFiles.set(path, file.content);
}
}
}

return {
...config,
content: allTsxFiles.map((content) => ({
content: [...allTsxFiles.values()].map((content) => ({
raw: content,
extension: "tsx",
})),
Expand All @@ -131,7 +147,7 @@ export const plugin = (config?: Config): Plugin => {
from: FROM,
mode,
config: config
? withReleaseContent(config)
? await withReleaseContent(config)
: await loadTailwindConfig(root),
}).catch(() => ""));

Expand All @@ -151,7 +167,7 @@ export const plugin = (config?: Config): Plugin => {
css = await bundle({
from: FROM,
mode,
config: withReleaseContent(config),
config: await withReleaseContent(config),
});

lru.set(revision, css);
Expand Down

0 comments on commit ba608d4

Please sign in to comment.