Package library as @moduletrace/twui npm package
- Move components/ to src/ and compile to dist/ with tsc - Add root barrel export plus deep-import subpath exports and base.css export - Post-compile step: copy base.css, add .js import extensions, mark client modules - Add publish.sh and .npmrc for the Gitea npm registry - Fix type errors, guard TinyMCE env access, drop ace-builds type dependency - Rename clashing props type names, remove Next.js scaffolding and unused deps Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
@@ -0,0 +1,80 @@
|
||||
/**
|
||||
* Runs after `tsc`:
|
||||
* 1. Copies `src/base.css` into `dist/` (tsc only emits TS output).
|
||||
* 2. Adds file extensions to relative imports (see addExtensions).
|
||||
* 3. Prepends `"use client"` to component/hook modules so they work as
|
||||
* client components under the Next.js App Router. Pure modules (utils,
|
||||
* types, the barrel) are left alone so they stay usable on the server.
|
||||
*/
|
||||
import { Glob } from "bun";
|
||||
import path from "node:path";
|
||||
|
||||
const root = path.resolve(import.meta.dir, "..");
|
||||
const dist = path.join(root, "dist");
|
||||
|
||||
await Bun.write(
|
||||
path.join(dist, "base.css"),
|
||||
Bun.file(path.join(root, "src", "base.css")),
|
||||
);
|
||||
|
||||
/**
|
||||
* tsc keeps extensionless relative specifiers ("./layout/Button"), which only
|
||||
* bundlers resolve. Rewrite them to full paths so plain Node ESM (e.g. SSR
|
||||
* with the package left external) can load the output too.
|
||||
*/
|
||||
async function resolveSpecifier(fromFile: string, specifier: string) {
|
||||
if (/\.(js|css|json)$/.test(specifier)) return specifier;
|
||||
|
||||
const base = path.resolve(path.dirname(fromFile), specifier);
|
||||
|
||||
if (await Bun.file(`${base}.js`).exists()) return `${specifier}.js`;
|
||||
if (await Bun.file(path.join(base, "index.js")).exists()) {
|
||||
return `${specifier}/index.js`;
|
||||
}
|
||||
|
||||
return specifier;
|
||||
}
|
||||
|
||||
const RELATIVE_IMPORT =
|
||||
/(\bfrom\s*|\bimport\s*\(\s*|\bimport\s+)(["'])(\.{1,2}\/[^"']*)\2/g;
|
||||
|
||||
async function addExtensions(filePath: string, content: string) {
|
||||
let result = "";
|
||||
let lastIndex = 0;
|
||||
|
||||
for (const match of content.matchAll(RELATIVE_IMPORT)) {
|
||||
const [full, prefix, quote, specifier] = match;
|
||||
const resolved = await resolveSpecifier(filePath, specifier!);
|
||||
result += content.slice(lastIndex, match.index);
|
||||
result += `${prefix}${quote}${resolved}${quote}`;
|
||||
lastIndex = match.index! + full.length;
|
||||
}
|
||||
|
||||
return result + content.slice(lastIndex);
|
||||
}
|
||||
|
||||
const SERVER_SAFE = [/^index\.js$/, /^types\.js$/, /^utils\//, /^\(utils\)\//];
|
||||
|
||||
let count = 0;
|
||||
|
||||
for (const file of new Glob("**/*.{js,d.ts}").scanSync(dist)) {
|
||||
const filePath = path.join(dist, file);
|
||||
let content = await addExtensions(
|
||||
filePath,
|
||||
await Bun.file(filePath).text(),
|
||||
);
|
||||
|
||||
const isClientModule =
|
||||
file.endsWith(".js") &&
|
||||
!SERVER_SAFE.some((pattern) => pattern.test(file)) &&
|
||||
!content.startsWith('"use client"');
|
||||
|
||||
if (isClientModule) {
|
||||
content = `"use client";\n${content}`;
|
||||
count++;
|
||||
}
|
||||
|
||||
await Bun.write(filePath, content);
|
||||
}
|
||||
|
||||
console.log(`post-compile: copied base.css, marked ${count} client modules`);
|
||||
Reference in New Issue
Block a user