babel-plugin-saykit 0.6.0 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/catalogue-B3HA3aVz.cjs +63 -0
- package/dist/index.cjs +14 -29
- package/dist/index.d.cts +20 -6
- package/dist/metro/index.cjs +57 -0
- package/dist/metro/index.d.cts +27 -0
- package/dist/metro/transformer.cjs +38 -0
- package/dist/metro/transformer.d.cts +20 -0
- package/dist/next/index.cjs +51 -0
- package/dist/next/index.d.cts +38 -0
- package/dist/next/loader.cjs +25 -0
- package/dist/next/loader.d.cts +26 -0
- package/package.json +15 -3
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
let node_path = require("node:path");
|
|
2
|
+
let node_fs = require("node:fs");
|
|
3
|
+
let _saykit_config_features_catalogue = require("@saykit/config/features/catalogue");
|
|
4
|
+
//#region src/catalogue.ts
|
|
5
|
+
/** Normalise an absolute path into the form bucket globs are written against. */
|
|
6
|
+
const toId = (path) => (0, node_path.relative)(process.cwd(), path).replaceAll("\\", "/").split("?")[0];
|
|
7
|
+
/**
|
|
8
|
+
* The glob a bucket's catalogues match, e.g. `src/locales/*.po` — the `output`
|
|
9
|
+
* template with its placeholders filled in. Bundlers that select files by glob
|
|
10
|
+
* rather than by predicate need this to target exactly the catalogues, and
|
|
11
|
+
* nothing else sharing their extension.
|
|
12
|
+
*/
|
|
13
|
+
const catalogueGlob = (bucket) => String(bucket.output).replace("{locale}", "*").replace("{extension}", bucket.formatter.extension.slice(1)).replaceAll("\\", "/").replace(/^\.\//, "");
|
|
14
|
+
/** Whether `path` is one of the config's catalogue outputs. */
|
|
15
|
+
const isCatalogue = (config, path) => config.buckets.some((bucket) => bucket.output.match(toId(path)));
|
|
16
|
+
/**
|
|
17
|
+
* Assemble the catalogue at `path` into a `{ id: string }` record, merging in
|
|
18
|
+
* the fallback chain (configured fallbacks + the source locale) so an
|
|
19
|
+
* untranslated key resolves to a fallback string rather than going missing.
|
|
20
|
+
*
|
|
21
|
+
* Returns `undefined` when the path is not a catalogue, and otherwise reports
|
|
22
|
+
* the files that fed the record alongside it — a bundler that can track them
|
|
23
|
+
* gets invalidation for free when a fallback locale is edited.
|
|
24
|
+
*
|
|
25
|
+
* Reads are synchronous so every caller can share one implementation: Babel's
|
|
26
|
+
* visitor cannot await, and the handful of files in a fallback chain are not
|
|
27
|
+
* worth an async path in a bundler that is blocked on the result anyway.
|
|
28
|
+
*/
|
|
29
|
+
function loadCatalogue(config, path) {
|
|
30
|
+
const id = toId(path);
|
|
31
|
+
const bucket = config.buckets.find((b) => b.output.match(id));
|
|
32
|
+
if (!bucket) return;
|
|
33
|
+
const { sources } = (0, _saykit_config_features_catalogue.resolveCatalogueSources)(config, bucket, id);
|
|
34
|
+
return {
|
|
35
|
+
record: (0, _saykit_config_features_catalogue.assembleCatalogueRecord)(bucket, sources.map((source) => {
|
|
36
|
+
try {
|
|
37
|
+
return (0, node_fs.readFileSync)(source, "utf8");
|
|
38
|
+
} catch {
|
|
39
|
+
return "";
|
|
40
|
+
}
|
|
41
|
+
})),
|
|
42
|
+
sources
|
|
43
|
+
};
|
|
44
|
+
}
|
|
45
|
+
//#endregion
|
|
46
|
+
Object.defineProperty(exports, "catalogueGlob", {
|
|
47
|
+
enumerable: true,
|
|
48
|
+
get: function() {
|
|
49
|
+
return catalogueGlob;
|
|
50
|
+
}
|
|
51
|
+
});
|
|
52
|
+
Object.defineProperty(exports, "isCatalogue", {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
get: function() {
|
|
55
|
+
return isCatalogue;
|
|
56
|
+
}
|
|
57
|
+
});
|
|
58
|
+
Object.defineProperty(exports, "loadCatalogue", {
|
|
59
|
+
enumerable: true,
|
|
60
|
+
get: function() {
|
|
61
|
+
return loadCatalogue;
|
|
62
|
+
}
|
|
63
|
+
});
|
package/dist/index.cjs
CHANGED
|
@@ -1,44 +1,29 @@
|
|
|
1
|
-
|
|
1
|
+
const require_catalogue = require("./catalogue-B3HA3aVz.cjs");
|
|
2
2
|
let node_path = require("node:path");
|
|
3
3
|
let _babel_core = require("@babel/core");
|
|
4
|
-
let _saykit_config_features_catalogue = require("@saykit/config/features/catalogue");
|
|
5
4
|
let _saykit_config_features_loader = require("@saykit/config/features/loader");
|
|
6
5
|
//#region src/index.ts
|
|
7
|
-
var src_default = (
|
|
6
|
+
var src_default = (_, { catalogues = "inline" } = {}) => {
|
|
8
7
|
const config = (0, _saykit_config_features_loader.resolveConfig)();
|
|
9
8
|
return {
|
|
10
9
|
name: "saykit",
|
|
11
|
-
|
|
12
|
-
const id_ = opts.sourceFileName;
|
|
13
|
-
if (!id_ || id_.includes("node_modules")) return parse(code, opts);
|
|
14
|
-
const id = (0, node_path.relative)(process.cwd(), id_).replaceAll("\\", "/").split("?")[0];
|
|
15
|
-
return parse(config.buckets.find((b) => b.match(id))?.transformer.transform(code, id) ?? code, opts);
|
|
16
|
-
},
|
|
17
|
-
visitor: { ImportDeclaration(path, state) {
|
|
10
|
+
visitor: catalogues === "module" ? {} : { ImportDeclaration(path, state) {
|
|
18
11
|
const importee = path.node.source.value;
|
|
19
12
|
if (!importee.startsWith(".")) return;
|
|
20
13
|
const importer = state.filename ?? state.file.opts.filename;
|
|
21
14
|
if (!importer) return;
|
|
22
|
-
const
|
|
15
|
+
const catalogue = require_catalogue.loadCatalogue(config, (0, node_path.resolve)((0, node_path.dirname)(importer), importee));
|
|
16
|
+
if (!catalogue) return;
|
|
17
|
+
const [specifier, ...rest] = path.node.specifiers;
|
|
18
|
+
if (specifier?.type !== "ImportDefaultSpecifier" || rest.length > 0) throw path.buildCodeFrameError("SayKit inline imports require a single default import");
|
|
19
|
+
path.replaceWith(_babel_core.types.variableDeclaration("const", [_babel_core.types.variableDeclarator(_babel_core.types.identifier(specifier.local.name), _babel_core.types.objectExpression(Object.entries(catalogue.record).map(([key, value]) => _babel_core.types.objectProperty(_babel_core.types.stringLiteral(key), _babel_core.types.stringLiteral(value)))))]));
|
|
20
|
+
} },
|
|
21
|
+
parserOverride(code, opts, parse) {
|
|
22
|
+
const id_ = opts.sourceFileName;
|
|
23
|
+
if (!id_ || id_.includes("node_modules")) return parse(code, opts);
|
|
23
24
|
const id = (0, node_path.relative)(process.cwd(), id_).replaceAll("\\", "/").split("?")[0];
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
const specifier = path.node.specifiers.find((s) => s.type === "ImportDefaultSpecifier");
|
|
27
|
-
if (!specifier) throw path.buildCodeFrameError("SayKit inline imports require a default import");
|
|
28
|
-
const { sources } = (0, _saykit_config_features_catalogue.resolveCatalogueSources)(config, bucket, id);
|
|
29
|
-
const contents = sources.map((source) => {
|
|
30
|
-
try {
|
|
31
|
-
return (0, node_fs.readFileSync)(source, "utf8");
|
|
32
|
-
} catch {
|
|
33
|
-
return "";
|
|
34
|
-
}
|
|
35
|
-
});
|
|
36
|
-
for (const source of sources) try {
|
|
37
|
-
api.addExternalDependency(source);
|
|
38
|
-
} catch {}
|
|
39
|
-
const entries = Object.entries((0, _saykit_config_features_catalogue.assembleCatalogueRecord)(bucket, contents));
|
|
40
|
-
path.replaceWith(_babel_core.types.variableDeclaration("const", [_babel_core.types.variableDeclarator(_babel_core.types.identifier(specifier.local.name), _babel_core.types.objectExpression(entries.map(([key, value]) => _babel_core.types.objectProperty(_babel_core.types.stringLiteral(key), _babel_core.types.stringLiteral(value)))))]));
|
|
41
|
-
} }
|
|
25
|
+
return parse(config.buckets.find((b) => b.match(id))?.transformer.transform(code, id) ?? code, opts);
|
|
26
|
+
}
|
|
42
27
|
};
|
|
43
28
|
};
|
|
44
29
|
//#endregion
|
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,26 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { PluginObj, parse } from "@babel/core";
|
|
2
2
|
//#region src/index.d.ts
|
|
3
3
|
declare module '@babel/core' {
|
|
4
|
-
interface PluginAPI {
|
|
5
|
-
addExternalDependency(ref: string): void;
|
|
6
|
-
}
|
|
7
4
|
interface PluginObj {
|
|
8
5
|
parserOverride(code: string, opts: TransformOptions, parse: typeof parse): ParseResult | null;
|
|
9
6
|
}
|
|
10
7
|
}
|
|
11
|
-
|
|
12
|
-
|
|
8
|
+
interface Options {
|
|
9
|
+
/**
|
|
10
|
+
* How a catalogue import is resolved.
|
|
11
|
+
*
|
|
12
|
+
* `'inline'` (the default) replaces the import with the assembled record, so
|
|
13
|
+
* the Babel plugin is enough on its own. The cost is that the record lands in
|
|
14
|
+
* the importing module, which a bundler only re-reads when that module's own
|
|
15
|
+
* bytes change — editing a catalogue will not hot-reload.
|
|
16
|
+
*
|
|
17
|
+
* `'module'` leaves the import for a bundler integration to serve —
|
|
18
|
+
* `babel-plugin-saykit/next` or `babel-plugin-saykit/metro`. Set this
|
|
19
|
+
* whenever one of those is wired up, or the import gets inlined before the
|
|
20
|
+
* integration is ever asked for the module.
|
|
21
|
+
*/
|
|
22
|
+
catalogues?: 'inline' | 'module';
|
|
23
|
+
}
|
|
24
|
+
declare const _default: (_: unknown, { catalogues }?: Options) => PluginObj;
|
|
25
|
+
//#endregion
|
|
26
|
+
export { Options, _default as default };
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
let node_path = require("node:path");
|
|
3
|
+
let _saykit_config_features_loader = require("@saykit/config/features/loader");
|
|
4
|
+
let node_module = require("node:module");
|
|
5
|
+
//#region src/metro/index.ts
|
|
6
|
+
/**
|
|
7
|
+
* Resolve Metro's own transformer from the project rather than from this
|
|
8
|
+
* package, since it is the project that depends on Metro.
|
|
9
|
+
*
|
|
10
|
+
* The base is the config's `projectRoot` — `process.cwd()` is wrong whenever
|
|
11
|
+
* Metro is started from elsewhere, which in a monorepo is the normal case.
|
|
12
|
+
*/
|
|
13
|
+
const transformerPath = (0, node_path.join)(__dirname, "transformer.cjs");
|
|
14
|
+
function resolveUpstream(specifier, projectRoot) {
|
|
15
|
+
if ((0, node_path.isAbsolute)(specifier)) return specifier;
|
|
16
|
+
try {
|
|
17
|
+
return (0, node_module.createRequire)((0, node_path.join)(projectRoot, "metro.config.js")).resolve(specifier);
|
|
18
|
+
} catch {
|
|
19
|
+
return require.resolve(specifier);
|
|
20
|
+
}
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* Wrap a Metro config so SayKit catalogues load as real modules.
|
|
24
|
+
*
|
|
25
|
+
* ```js
|
|
26
|
+
* const { withSayKit } = require('babel-plugin-saykit/metro');
|
|
27
|
+
* module.exports = withSayKit(getDefaultConfig(__dirname));
|
|
28
|
+
* ```
|
|
29
|
+
*
|
|
30
|
+
* Metro's transform cache is keyed on each file's own bytes, so a catalogue has
|
|
31
|
+
* to stay a module of its own to be invalidated at all — see
|
|
32
|
+
* `./transformer.ts`.
|
|
33
|
+
*
|
|
34
|
+
* Apply this outermost. Anything wrapped around it that also sets
|
|
35
|
+
* `transformerPath` replaces this one, and catalogues stop being assembled.
|
|
36
|
+
*/
|
|
37
|
+
function withSayKit(metroConfig) {
|
|
38
|
+
if (metroConfig.transformerPath === transformerPath) return metroConfig;
|
|
39
|
+
const extensions = (0, _saykit_config_features_loader.resolveConfig)().buckets.map((bucket) => bucket.formatter.extension.slice(1)).filter((extension) => extension !== "json");
|
|
40
|
+
const sourceExts = [...metroConfig.resolver?.sourceExts ?? []];
|
|
41
|
+
for (const extension of extensions) if (!sourceExts.includes(extension)) sourceExts.push(extension);
|
|
42
|
+
const upstream = metroConfig.transformerPath ?? "metro-transform-worker";
|
|
43
|
+
return {
|
|
44
|
+
...metroConfig,
|
|
45
|
+
resolver: {
|
|
46
|
+
...metroConfig.resolver,
|
|
47
|
+
sourceExts
|
|
48
|
+
},
|
|
49
|
+
transformerPath,
|
|
50
|
+
transformer: {
|
|
51
|
+
...metroConfig.transformer,
|
|
52
|
+
saykitTransformerPath: resolveUpstream(upstream, metroConfig.projectRoot ?? process.cwd())
|
|
53
|
+
}
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
//#endregion
|
|
57
|
+
exports.withSayKit = withSayKit;
|
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
//#region src/metro/index.d.ts
|
|
2
|
+
interface MetroConfig {
|
|
3
|
+
projectRoot?: string;
|
|
4
|
+
resolver?: {
|
|
5
|
+
sourceExts?: string[];
|
|
6
|
+
};
|
|
7
|
+
transformer?: Record<string, unknown>;
|
|
8
|
+
transformerPath?: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Wrap a Metro config so SayKit catalogues load as real modules.
|
|
12
|
+
*
|
|
13
|
+
* ```js
|
|
14
|
+
* const { withSayKit } = require('babel-plugin-saykit/metro');
|
|
15
|
+
* module.exports = withSayKit(getDefaultConfig(__dirname));
|
|
16
|
+
* ```
|
|
17
|
+
*
|
|
18
|
+
* Metro's transform cache is keyed on each file's own bytes, so a catalogue has
|
|
19
|
+
* to stay a module of its own to be invalidated at all — see
|
|
20
|
+
* `./transformer.ts`.
|
|
21
|
+
*
|
|
22
|
+
* Apply this outermost. Anything wrapped around it that also sets
|
|
23
|
+
* `transformerPath` replaces this one, and catalogues stop being assembled.
|
|
24
|
+
*/
|
|
25
|
+
declare function withSayKit<T extends MetroConfig>(metroConfig: T): T;
|
|
26
|
+
//#endregion
|
|
27
|
+
export { withSayKit };
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_catalogue = require("../catalogue-B3HA3aVz.cjs");
|
|
3
|
+
let node_path = require("node:path");
|
|
4
|
+
let _saykit_config_features_loader = require("@saykit/config/features/loader");
|
|
5
|
+
let node_fs = require("node:fs");
|
|
6
|
+
let node_crypto = require("node:crypto");
|
|
7
|
+
//#region src/metro/transformer.ts
|
|
8
|
+
const config = (0, _saykit_config_features_loader.resolveConfig)();
|
|
9
|
+
const upstream = (transformerConfig) => require(transformerConfig.saykitTransformerPath);
|
|
10
|
+
/**
|
|
11
|
+
* What a catalogue transforms into depends on the SayKit config — the fallback
|
|
12
|
+
* chain it resolves, the formatter that parses it — and on the version of this
|
|
13
|
+
* package doing the assembling. Neither is a byte of the file Metro hashes, so
|
|
14
|
+
* without them in the cache key, editing `saykit.config.*` or upgrading leaves
|
|
15
|
+
* every catalogue serving the record it was cached with.
|
|
16
|
+
*/
|
|
17
|
+
const salt = (0, node_crypto.createHash)("sha1").update((0, node_fs.readFileSync)((0, node_path.join)(__dirname, "..", "..", "package.json"), "utf8")).update((0, node_fs.readFileSync)((0, _saykit_config_features_loader.resolveConfigFile)(), "utf8")).digest("hex");
|
|
18
|
+
/**
|
|
19
|
+
* Metro reads `.json` straight through `transformJSON` and never runs Babel over
|
|
20
|
+
* it, so a Babel plugin cannot reach a catalogue at all. Wrapping the transform
|
|
21
|
+
* worker is the one place that can: it substitutes the assembled record for the
|
|
22
|
+
* catalogue's own source, leaving the file a real module whose sha1 — and
|
|
23
|
+
* therefore Metro's transform cache entry — moves whenever it is edited.
|
|
24
|
+
*/
|
|
25
|
+
function transform(transformerConfig, projectRoot, filename, data, options) {
|
|
26
|
+
const worker = upstream(transformerConfig);
|
|
27
|
+
const catalogue = require_catalogue.loadCatalogue(config, filename);
|
|
28
|
+
if (!catalogue) return worker.transform(transformerConfig, projectRoot, filename, data, options);
|
|
29
|
+
const record = JSON.stringify(catalogue.record);
|
|
30
|
+
const code = filename.endsWith(".json") ? record : `module.exports = ${record};`;
|
|
31
|
+
return worker.transform(transformerConfig, projectRoot, filename, Buffer.from(code), options);
|
|
32
|
+
}
|
|
33
|
+
function getCacheKey(transformerConfig, options) {
|
|
34
|
+
return `${upstream(transformerConfig).getCacheKey(transformerConfig, options)}-saykit-${salt}`;
|
|
35
|
+
}
|
|
36
|
+
//#endregion
|
|
37
|
+
exports.getCacheKey = getCacheKey;
|
|
38
|
+
exports.transform = transform;
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
//#region src/metro/transformer.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The transformer config Metro hands each worker call. `saykitTransformerPath`
|
|
4
|
+
* is the upstream worker this one wraps, stashed here by {@link withSayKit}
|
|
5
|
+
* because a worker is loaded standalone and has no other way to reach it.
|
|
6
|
+
*/
|
|
7
|
+
interface TransformerConfig {
|
|
8
|
+
saykitTransformerPath: string;
|
|
9
|
+
}
|
|
10
|
+
/**
|
|
11
|
+
* Metro reads `.json` straight through `transformJSON` and never runs Babel over
|
|
12
|
+
* it, so a Babel plugin cannot reach a catalogue at all. Wrapping the transform
|
|
13
|
+
* worker is the one place that can: it substitutes the assembled record for the
|
|
14
|
+
* catalogue's own source, leaving the file a real module whose sha1 — and
|
|
15
|
+
* therefore Metro's transform cache entry — moves whenever it is edited.
|
|
16
|
+
*/
|
|
17
|
+
declare function transform(transformerConfig: TransformerConfig, projectRoot: string, filename: string, data: Buffer, options: unknown): Promise<unknown>;
|
|
18
|
+
declare function getCacheKey(transformerConfig: TransformerConfig, options?: unknown): string;
|
|
19
|
+
//#endregion
|
|
20
|
+
export { getCacheKey, transform };
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_catalogue = require("../catalogue-B3HA3aVz.cjs");
|
|
3
|
+
let _saykit_config_features_loader = require("@saykit/config/features/loader");
|
|
4
|
+
//#region src/next/index.ts
|
|
5
|
+
const loader = "babel-plugin-saykit/next/loader";
|
|
6
|
+
/**
|
|
7
|
+
* Wrap a Next config so SayKit catalogues load as real modules.
|
|
8
|
+
*
|
|
9
|
+
* ```js
|
|
10
|
+
* import { withSayKit } from 'babel-plugin-saykit/next';
|
|
11
|
+
* export default withSayKit({});
|
|
12
|
+
* ```
|
|
13
|
+
*
|
|
14
|
+
* Pair it with `catalogues: 'module'` on the Babel plugin — the plugin has to
|
|
15
|
+
* leave the import alone for the loader to ever be asked for the module.
|
|
16
|
+
*
|
|
17
|
+
* Rules are derived from the SayKit config, so both bundlers get one per bucket
|
|
18
|
+
* targeting exactly that bucket's `output` — nothing else sharing the extension
|
|
19
|
+
* goes through the loader, which matters most for a `.json` bucket, where the
|
|
20
|
+
* alternative would be routing every JSON import in the app through it.
|
|
21
|
+
*/
|
|
22
|
+
function withSayKit(nextConfig = {}) {
|
|
23
|
+
const config = (0, _saykit_config_features_loader.resolveConfig)();
|
|
24
|
+
const rules = Object.fromEntries(config.buckets.map((bucket) => [`**/${require_catalogue.catalogueGlob(bucket)}`, {
|
|
25
|
+
loaders: [loader],
|
|
26
|
+
as: "*.js"
|
|
27
|
+
}]));
|
|
28
|
+
return {
|
|
29
|
+
...nextConfig,
|
|
30
|
+
turbopack: {
|
|
31
|
+
...nextConfig.turbopack,
|
|
32
|
+
rules: {
|
|
33
|
+
...nextConfig.turbopack?.rules,
|
|
34
|
+
...rules
|
|
35
|
+
}
|
|
36
|
+
},
|
|
37
|
+
webpack: (webpackConfig, context) => {
|
|
38
|
+
const result = nextConfig.webpack?.(webpackConfig, context) ?? webpackConfig;
|
|
39
|
+
result.module ??= {};
|
|
40
|
+
result.module.rules ??= [];
|
|
41
|
+
result.module.rules.push({
|
|
42
|
+
test: (path) => require_catalogue.isCatalogue(config, path),
|
|
43
|
+
use: loader,
|
|
44
|
+
type: "javascript/auto"
|
|
45
|
+
});
|
|
46
|
+
return result;
|
|
47
|
+
}
|
|
48
|
+
};
|
|
49
|
+
}
|
|
50
|
+
//#endregion
|
|
51
|
+
exports.withSayKit = withSayKit;
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
//#region src/next/index.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The slice of Next's config this touches. Typed here rather than imported so
|
|
4
|
+
* the package does not depend on `next`.
|
|
5
|
+
*/
|
|
6
|
+
interface NextConfig {
|
|
7
|
+
turbopack?: {
|
|
8
|
+
rules?: Record<string, unknown>;
|
|
9
|
+
[key: string]: unknown;
|
|
10
|
+
};
|
|
11
|
+
webpack?: (config: WebpackConfig, context: unknown) => WebpackConfig;
|
|
12
|
+
[key: string]: unknown;
|
|
13
|
+
}
|
|
14
|
+
interface WebpackConfig {
|
|
15
|
+
module?: {
|
|
16
|
+
rules?: unknown[];
|
|
17
|
+
};
|
|
18
|
+
[key: string]: unknown;
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* Wrap a Next config so SayKit catalogues load as real modules.
|
|
22
|
+
*
|
|
23
|
+
* ```js
|
|
24
|
+
* import { withSayKit } from 'babel-plugin-saykit/next';
|
|
25
|
+
* export default withSayKit({});
|
|
26
|
+
* ```
|
|
27
|
+
*
|
|
28
|
+
* Pair it with `catalogues: 'module'` on the Babel plugin — the plugin has to
|
|
29
|
+
* leave the import alone for the loader to ever be asked for the module.
|
|
30
|
+
*
|
|
31
|
+
* Rules are derived from the SayKit config, so both bundlers get one per bucket
|
|
32
|
+
* targeting exactly that bucket's `output` — nothing else sharing the extension
|
|
33
|
+
* goes through the loader, which matters most for a `.json` bucket, where the
|
|
34
|
+
* alternative would be routing every JSON import in the app through it.
|
|
35
|
+
*/
|
|
36
|
+
declare function withSayKit<T extends NextConfig>(nextConfig?: T): T & Required<Pick<NextConfig, 'turbopack' | 'webpack'>>;
|
|
37
|
+
//#endregion
|
|
38
|
+
export { withSayKit };
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
const require_catalogue = require("../catalogue-B3HA3aVz.cjs");
|
|
2
|
+
//#region src/next/loader.ts
|
|
3
|
+
const config = (0, require("@saykit/config/features/loader").resolveConfig)();
|
|
4
|
+
/**
|
|
5
|
+
* Replaces a catalogue file with its assembled record.
|
|
6
|
+
*
|
|
7
|
+
* This is a *loader* rather than a plugin because Turbopack runs loaders and
|
|
8
|
+
* not webpack plugins, and it is published only so `withSayKit` can name it in
|
|
9
|
+
* the rules it generates. A plain webpack, Vite or Rollup build should reach for
|
|
10
|
+
* `unplugin-saykit` instead, which does the same job through each bundler's own
|
|
11
|
+
* plugin API.
|
|
12
|
+
*
|
|
13
|
+
* The catalogue stays a real module, which is the whole point: the importer
|
|
14
|
+
* keeps a dependency edge to it, so editing a catalogue invalidates exactly the
|
|
15
|
+
* modules that read it. Inlining the record into the importer instead leaves the
|
|
16
|
+
* importer's own bytes unchanged, and no bundler can invalidate on that.
|
|
17
|
+
*/
|
|
18
|
+
function saykitLoader(source) {
|
|
19
|
+
const catalogue = require_catalogue.loadCatalogue(config, this.resourcePath);
|
|
20
|
+
if (!catalogue) return source;
|
|
21
|
+
for (const file of catalogue.sources) this.addDependency(file);
|
|
22
|
+
return `export default ${JSON.stringify(catalogue.record)}`;
|
|
23
|
+
}
|
|
24
|
+
//#endregion
|
|
25
|
+
module.exports = saykitLoader;
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
//#region src/next/loader.d.ts
|
|
2
|
+
/**
|
|
3
|
+
* The slice of webpack's loader context this needs. Typing it here rather than
|
|
4
|
+
* depending on `webpack` keeps the package free of a bundler dependency, and
|
|
5
|
+
* Turbopack implements the same surface for the loaders it runs.
|
|
6
|
+
*/
|
|
7
|
+
interface LoaderContext {
|
|
8
|
+
resourcePath: string;
|
|
9
|
+
addDependency(file: string): void;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* Replaces a catalogue file with its assembled record.
|
|
13
|
+
*
|
|
14
|
+
* This is a *loader* rather than a plugin because Turbopack runs loaders and
|
|
15
|
+
* not webpack plugins, and it is published only so `withSayKit` can name it in
|
|
16
|
+
* the rules it generates. A plain webpack, Vite or Rollup build should reach for
|
|
17
|
+
* `unplugin-saykit` instead, which does the same job through each bundler's own
|
|
18
|
+
* plugin API.
|
|
19
|
+
*
|
|
20
|
+
* The catalogue stays a real module, which is the whole point: the importer
|
|
21
|
+
* keeps a dependency edge to it, so editing a catalogue invalidates exactly the
|
|
22
|
+
* modules that read it. Inlining the record into the importer instead leaves the
|
|
23
|
+
* importer's own bytes unchanged, and no bundler can invalidate on that.
|
|
24
|
+
*/
|
|
25
|
+
declare function saykitLoader(this: LoaderContext, source: string): string;
|
|
26
|
+
export = saykitLoader;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "babel-plugin-saykit",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.7.0",
|
|
4
4
|
"description": "Babel plugin for SayKit",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"i18n",
|
|
@@ -27,8 +27,20 @@
|
|
|
27
27
|
"type": "module",
|
|
28
28
|
"exports": {
|
|
29
29
|
".": {
|
|
30
|
-
"types": "./dist/index.d.
|
|
30
|
+
"types": "./dist/index.d.cts",
|
|
31
31
|
"default": "./dist/index.cjs"
|
|
32
|
+
},
|
|
33
|
+
"./metro": {
|
|
34
|
+
"types": "./dist/metro/index.d.cts",
|
|
35
|
+
"default": "./dist/metro/index.cjs"
|
|
36
|
+
},
|
|
37
|
+
"./next": {
|
|
38
|
+
"types": "./dist/next/index.d.cts",
|
|
39
|
+
"default": "./dist/next/index.cjs"
|
|
40
|
+
},
|
|
41
|
+
"./next/loader": {
|
|
42
|
+
"types": "./dist/next/loader.d.cts",
|
|
43
|
+
"default": "./dist/next/loader.cjs"
|
|
32
44
|
}
|
|
33
45
|
},
|
|
34
46
|
"publishConfig": {
|
|
@@ -37,7 +49,7 @@
|
|
|
37
49
|
},
|
|
38
50
|
"dependencies": {
|
|
39
51
|
"@babel/core": "^7.29.7",
|
|
40
|
-
"@saykit/config": "^0.
|
|
52
|
+
"@saykit/config": "^0.7.0"
|
|
41
53
|
},
|
|
42
54
|
"devDependencies": {
|
|
43
55
|
"@types/babel__core": "^7.20.5"
|