Skip to content

Commit

Permalink
feat: remove metro and add standalone mode
Browse files Browse the repository at this point in the history
  • Loading branch information
ceopaludetto committed Jun 22, 2024
1 parent dd304fa commit 29568d1
Show file tree
Hide file tree
Showing 16 changed files with 343 additions and 1,015 deletions.
10 changes: 10 additions & 0 deletions .changeset/honest-jobs-compare.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
---
"@diacritic/core": patch
"@diacritic/detector": patch
"@diacritic/parser": patch
"@diacritic/react": patch
"@diacritic/runtime": patch
"@diacritic/utilities": patch
---

Remove metro and add standalone mode
28 changes: 11 additions & 17 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -45,16 +45,6 @@
"default": "./dist/farm.cjs"
}
},
"./metro": {
"import": {
"types": "./dist/metro.d.ts",
"default": "./dist/metro.js"
},
"require": {
"types": "./dist/metro.d.cts",
"default": "./dist/metro.cjs"
}
},
"./rollup": {
"import": {
"types": "./dist/rollup.d.ts",
Expand All @@ -75,6 +65,16 @@
"default": "./dist/rspack.cjs"
}
},
"./standalone": {
"import": {
"types": "./dist/standalone.d.ts",
"default": "./dist/standalone.js"
},
"require": {
"types": "./dist/standalone.d.cts",
"default": "./dist/standalone.cjs"
}
},
"./vite": {
"import": {
"types": "./dist/vite.d.ts",
Expand Down Expand Up @@ -107,28 +107,22 @@
"lint": "eslint src"
},
"peerDependencies": {
"metro-transform-worker": "^0.80.0",
"vite": "^5.0.0"
},
"peerDependenciesMeta": {
"metro-transform-worker": {
"optional": true
},
"vite": {
"optional": true
}
},
"dependencies": {
"@diacritic/utilities": "workspace:*",
"@parcel/watcher": "^2.4.1",
"dset": "^3.1.3",
"glob": "^10.3.15",
"tiny-invariant": "^1.3.3",
"unplugin": "^1.10.1"
},
"devDependencies": {
"@chialab/esbuild-plugin-meta-url": "^0.18.2",
"metro-config": "^0.80.9",
"metro-transform-worker": "^0.80.9",
"vite": "^5.2.11"
}
}
27 changes: 11 additions & 16 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,40 +15,35 @@ import {
resourceWithoutPrefix,
} from "./utilities/loader";
import { ResourceGraph } from "./utilities/resource";
import { watchResources } from "./utilities/watcher";

export const diacritic = createUnplugin<DiacriticOptions>(({
defaultLanguage,
generation,
languages,
parser,
reactNative,
resources,
}, meta) => {
const resourceGraph = new ResourceGraph(languages, resources);
const processedIDs = new Set<string>();

let ran = false;
let unsubscribe!: () => Promise<void[]>;

return {
name: "diacritic",
enforce: "pre",
buildStart() {
async buildStart() {
if (meta.framework !== "esbuild")
for (const file of resourceGraph.allFiles()) this.addWatchFile(file);

if (meta.framework !== "esbuild" || !ran) {
generateTypes({ defaultLanguage, generation, languages, parser, resourceGraph });

ran = true;
}
unsubscribe = await watchResources({
resourceGraph,
resources,
onChange: async () => generateTypes({ defaultLanguage, generation, languages, parser, resourceGraph }),
});
},
watchChange: (id, change) => {
if (!isResource(id, resources)) return;

if (change.event === "create") resourceGraph.addFile(id, resources);
if (change.event === "delete") resourceGraph.removeFile(id, resources);

generateTypes({ defaultLanguage, generation, languages, parser, resourceGraph });
async buildEnd() {
await unsubscribe();
},
resolveId: (id) => {
if (isTranslationPath(id)) return normalizeTranslationPath(id) + ".ts";
Expand All @@ -70,7 +65,7 @@ export const diacritic = createUnplugin<DiacriticOptions>(({
// import { defaultLanguage, languages } from "virtual:translations/registry";
// ```
if (extract.mode === "registry")
return { code: createRegistry(defaultLanguage, languages, resourceGraph.allNamespaces(), reactNative) };
return { code: createRegistry(defaultLanguage, languages, resourceGraph.allNamespaces()) };

// In the language only mode, we want to export all namespaces from that language.
// Example:
Expand Down
66 changes: 0 additions & 66 deletions packages/core/src/metro.ts

This file was deleted.

65 changes: 65 additions & 0 deletions packages/core/src/standalone.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
import type { DiacriticGenerationOptions, DiacriticOptions } from "./utilities/types";

import { existsSync, readFileSync } from "node:fs";
import { EOL } from "node:os";
import { dirname, resolve } from "node:path";

import { createFunctionFromEntry, createRegistry, generateTypes } from "./utilities/generator";
import { createFolderAndFile } from "./utilities/loader";
import { ResourceGraph } from "./utilities/resource";
import { watchResources } from "./utilities/watcher";

type StartDiacriticOptions = DiacriticOptions & {
runtimeGeneration: DiacriticGenerationOptions;
};

export async function startDiacritic({
defaultLanguage,
generation,
languages,
parser,
resources,
runtimeGeneration,
}: StartDiacriticOptions) {
const resourceGraph = new ResourceGraph(languages, resources);

const unsubscribe = await watchResources({
resourceGraph,
resources,
onChange: async (event) => {
const shouldCreateRegistry = !event || event.type === "create" || event.type === "delete";

await generateTypes({ defaultLanguage, generation, languages, parser, resourceGraph });

if (shouldCreateRegistry) {
const registry = createRegistry(defaultLanguage, languages, resourceGraph.allNamespaces(), "./");
await createFolderAndFile(runtimeGeneration.outFile, registry);
}

for (const language of languages) {
for (const namespace of resourceGraph.allNamespaces()) {
const files = resourceGraph.allEntriesForLanguageAndNamespace(language, namespace)
.filter(file => existsSync(file))
.map(file => readFileSync(file, "utf-8"));

const entries = files.flatMap(item => parser.convertFile(item));
const functions = entries.map(item => createFunctionFromEntry(item));

functions.unshift(
...(runtimeGeneration?.banner ?? []),
`import type { Proxy } from "@diacritic/runtime"`,
"",
);

const dir = dirname(runtimeGeneration.outFile);
const out = resolve(dir, `${language}/${namespace}.ts`);

await createFolderAndFile(out, functions.join(EOL));
}
}
},
});

process.on("SIGINT", async () => unsubscribe());
process.on("SIGTERM", async () => unsubscribe());
}
23 changes: 14 additions & 9 deletions packages/core/src/utilities/generator/modules.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ import { EOL } from "node:os";

import { prefixes } from "../loader";

function toUnion(arr: string[]) {
return arr.map(item => `"${item}"`).join(" | ");
}

export function createFunctionFromEntry(entry: Entry) {
const r = Array.isArray(entry.return)
? "[" + entry.return.map(item => `\`${item}\``).join(", ") + "]"
Expand All @@ -24,26 +28,27 @@ export function createRegistry(
defaultLanguage: string,
languages: string[],
namespaces: string[],
reactNative = false,
prefix = prefixes[0],
) {
const importType = reactNative ? "() => require" : "async () => import";

return [
`export const defaultLanguage = ${JSON.stringify(defaultLanguage)} as const;`,
`export const languages = ${JSON.stringify(languages)} as const;`,
`export const namespaces = ${JSON.stringify(namespaces)} as const;`,
`export const defaultLanguage: ${JSON.stringify(defaultLanguage)} = ${JSON.stringify(defaultLanguage)};`,
`export const languages: (${toUnion(languages)})[] = ${JSON.stringify(languages)};`,
`export const namespaces: (${toUnion(namespaces)})[] = ${JSON.stringify(namespaces)};`,
"",
`const modules = {`,
languages.flatMap(language => [
`\t${language}: {`,
namespaces
.map(namespace => `\t\t\t${namespace}: ${importType}("${prefixes[0]}${language}/${namespace}"),`)
.map(namespace => `\t\t${namespace}: async () => import("${prefix}${language}/${namespace}"),`)
.join(EOL),
`\t},`,
]).join(EOL),
`}`,
`} as const;`,
"",
`export async function importTranslationModule(language: string, namespace: string) {`,
`export async function importTranslationModule(`,
`\tlanguage: (typeof languages)[number],`,
`\tnamespace: (typeof namespaces)[number]`,
`) {`,
`\treturn modules[language][namespace]();`,
`}`,
].join(EOL);
Expand Down
20 changes: 9 additions & 11 deletions packages/core/src/utilities/generator/types.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
import type { ResourceGraph } from "../resource";
import type { DiacriticGenerationOptions, Entry, Parser } from "../types";

import { existsSync, mkdirSync, readFileSync, writeFileSync } from "node:fs";
import { readFile } from "node:fs/promises";
import { EOL } from "node:os";
import { dirname } from "node:path";

import { dset } from "dset";

import { prefixes } from "../loader";
import { createFolderAndFile, prefixes } from "../loader";

/// keep-sorted
type GenerateTypesOptions = {
Expand All @@ -28,7 +27,7 @@ function functionFromEntry(entry: Entry) {
return `(${args}) => ${Array.isArray(entry.return) ? "string[]" : "string"}`;
}

export function generateTypes({
export async function generateTypes({
defaultLanguage,
generation,
languages,
Expand All @@ -38,7 +37,7 @@ export function generateTypes({
const entries = resourceGraph.allEntriesForLanguage(defaultLanguage);
const namespaces = resourceGraph.allNamespaces();

const declarations: string[] = generation?.banner ?? [];
const declarations: string[] = [...(generation?.banner ?? [])];

declarations.push(
`declare module "${prefixes[1]}registry" {`,
Expand All @@ -60,10 +59,12 @@ export function generateTypes({
);

for (const [namespace, files] of Object.entries(entries)) {
const contents = files.map(file => readFileSync(file, "utf-8")).flatMap(parser.convertFile);
const contents = await Promise.all(files.map(file => readFile(file, "utf-8")));
const entries = contents.flatMap(parser.convertFile);

const structure = {};

for (const entry of contents) dset(structure, entry.path, functionFromEntry(entry));
for (const entry of entries) dset(structure, entry.path, functionFromEntry(entry));

declarations.push(
`\t\t${namespace}: ${JSON.stringify(structure).replace(/"/g, "")};`,
Expand All @@ -78,8 +79,5 @@ export function generateTypes({
`}`,
);

const folder = dirname(generation.outFile);
if (!existsSync(folder)) mkdirSync(folder, { recursive: true });

writeFileSync(generation.outFile, declarations.join(EOL), "utf-8");
await createFolderAndFile(generation.outFile, declarations.join(EOL));
}
11 changes: 11 additions & 0 deletions packages/core/src/utilities/loader.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
import { existsSync } from "node:fs";
import { mkdir, writeFile } from "node:fs/promises";
import { dirname } from "node:path";

import invariant from "tiny-invariant";

export const prefixes = ["/~translations/", "~translations/", "virtual:translations/", "virtual/translations/"];
Expand Down Expand Up @@ -42,3 +46,10 @@ export function isResource(file: string, resources: string[]) {

return false;
}

export async function createFolderAndFile(path: string, content: string) {
const folder = dirname(path);
if (!existsSync(folder)) await mkdir(folder, { recursive: true });

await writeFile(path, content, "utf-8");
}
Loading

0 comments on commit 29568d1

Please sign in to comment.