diff --git a/src/core/codegen/dts.js b/src/core/codegen/dts.js index d4cab5d..503ebf2 100644 --- a/src/core/codegen/dts.js +++ b/src/core/codegen/dts.js @@ -11,7 +11,7 @@ export function generateDTS(localeDictionaries) { const dts = new DTSBuilder(); dts.setDisclaimer( - "FILE AUTOGENERATED BY t18s\nYou can safely add this to your .gitignore", + "FILE AUTOGENERATED BY t18s\nYou can safely add this to your .gitignore" ); dts.addModule(VIRTUAL_MODULE_PREFIX, (module) => { @@ -20,7 +20,7 @@ export function generateDTS(localeDictionaries) { module.addStatement( `export type Locale = ${stringTypeUnion(locales)};`, - (s) => s.setDescription("The known locales"), + (s) => s.setDescription("The known locales") ); module.addStatement( @@ -29,32 +29,32 @@ export function generateDTS(localeDictionaries) { .join(",")}]>;`, (s) => s.setDescription( - "A store containing the available locales.\n\nThis store will only ever change during development, it is constant in production.", - ), + "A store containing the available locales.\n\nThis store will only ever change during development, it is constant in production." + ) ); module.addStatement(`export const locale: Writable;`, (s) => - s.setDescription("The current locale"), + s.setDescription("The current locale") ); module.addStatement( "export function setLocale(newLocale: Locale): void;", (s) => s.setDescription( - "Set the current locale. Equivalent to `locale.set(newLocale)`\n@param newLocale The new locale", - ), + "Set the current locale. Equivalent to `locale.set(newLocale)`\n@param newLocale The new locale" + ) ); module.addStatement("export const isLoading: Readable;", (s) => - s.setDescription("If the current locale is still being loaded."), + s.setDescription("If the current locale is still being loaded.") ); module.addStatement( "export function init(options: { initialLocale: Locale, fallbackLocale?: Locale, loadingDelay?: number }) : Promise", (s) => s.setDescription( - "Initialize t18s.\nThis must be called before any other t18s function.", - ), + "Initialize t18s.\nThis must be called before any other t18s function." + ) ); module.addStatement( @@ -66,28 +66,16 @@ export function generateDTS(localeDictionaries) { "This can be used to anticipate a locale change.", "", "Maybe preload the locale of the user's browser, since they're likely to switch to that.", - ].join("\n"), - ), + ].join("\n") + ) ); module.addStatement( "export const isLocale: (maybeLocale: unknown) => maybeLocale is Locale;", (s) => s.setDescription( - "Convenience function to check if something is a valid locale.", - ), - ); - - module.addStatement( - "export const t : Writable<(key: Key, ...values: (Messages[Key] extends undefined ? [(undefined | {})?] : [Messages[Key]])) => string>;", - (s) => - s.setDescription( - [ - "The translation function.", - "@param key A translation key.", - "@param values Any values that are interpolated into the translation.", - ].join("\n"), - ), + "Convenience function to check if something is a valid locale." + ) ); let messagesType = "export type Messages = {\n"; @@ -101,8 +89,30 @@ export function generateDTS(localeDictionaries) { messagesType += "};"; module.addStatement(messagesType, (s) => - s.setDescription("Available Translations and their Arguments"), + s.setDescription("Available Translations and their Arguments") + ); + + // t Store + module.addStatement( + "export const t : Writable<(key: Key, ...values: (Messages[Key] extends undefined ? [(undefined | {})?] : [Messages[Key]])) => string>;", + (s) => + s.setDescription( + [ + "The translation store.", + "@param key A translation key.", + "@param values Any values that are interpolated into the translation.", + ].join("\n") + ) + ); + + + // T Component + module.addImport( + "import type { SvelteComponentTyped } from 'svelte';" ); + module.addStatement("export class T extends SvelteComponentTyped { };", s => { + s.setDescription("The t18s translation component.") + }); }); return dts.build(); diff --git a/src/core/codegen/main.js b/src/core/codegen/main.js index 5e1903d..74ff676 100644 --- a/src/core/codegen/main.js +++ b/src/core/codegen/main.js @@ -9,6 +9,7 @@ export function generateMainModuleCode(localesIterable, verbose) { return ` import { writable, get } from 'svelte/store'; + export { T } from "$t18s/runtime"; const messages = {} diff --git a/src/core/codegen/utils/client-side-console.js b/src/core/codegen/utils/client-side-console.js deleted file mode 100644 index 7733435..0000000 --- a/src/core/codegen/utils/client-side-console.js +++ /dev/null @@ -1,24 +0,0 @@ -/** @param {string} msg */ -export function debug(msg) { - return `console.debug("${formatMessage(msg)}")`; -} - -/** @param {string} msg */ -export function log(msg) { - return `console.log("${formatMessage(msg)}")`; -} - -/** @param {string} msg */ -export function warn(msg) { - return `console.warn("${formatMessage(msg)}")`; -} - -/** @param {string} msg */ -export function error(msg) { - return `console.error("${formatMessage(msg)}")`; -} - -/** @param {string} msg */ -function formatMessage(msg) { - return "[t18s] " + msg; -} diff --git a/src/core/codegen/utils/dtsBuilder.js b/src/core/codegen/utils/dtsBuilder.js index 23f45b8..a6572d2 100644 --- a/src/core/codegen/utils/dtsBuilder.js +++ b/src/core/codegen/utils/dtsBuilder.js @@ -73,6 +73,7 @@ class Module { */ addStatement(code, statementBuilder = undefined) { const statement = new Statement(code); + if (statementBuilder) { statementBuilder(statement); } diff --git a/src/core/codegen/utils/stringUtils.js b/src/core/codegen/utils/stringUtils.js index 851adf7..915ccad 100644 --- a/src/core/codegen/utils/stringUtils.js +++ b/src/core/codegen/utils/stringUtils.js @@ -14,10 +14,16 @@ export const addParentheses = (str) => `(${str})`; /** * Returns a type union of the given strings - * @param {string[]} strings ["a","b","c"] + * @param {Iterable} strings The strings to union (e.g. ["a", "b", "c"]) * @returns "a" | "b" | "c" */ -export const stringTypeUnion = (strings) => strings.map(addQuotes).join("|"); +export const stringTypeUnion = (strings) => { + let union = ""; + for (const str of strings) { + union += `"${str}"|`; + } + return union.slice(0, -1); +} /** * Indents some text by one level. diff --git a/src/core/index.js b/src/core/index.js index 85b548b..6c3c919 100644 --- a/src/core/index.js +++ b/src/core/index.js @@ -1,5 +1,5 @@ -import { basename, resolve } from "path"; -import { readdir, writeFile } from "fs/promises"; +import { basename, dirname, resolve } from "node:path"; +import { readdir, writeFile } from "node:fs/promises"; import { YamlHandler } from "./file-handling/formats/yaml.js"; import { JsonHandler } from "./file-handling/formats/json.js"; import { Logger } from "./utils/logger.js"; @@ -17,6 +17,8 @@ import { Reporter } from "./utils/reporter.js"; import { ResultMatcher } from "./utils/resultMatcher.js"; import { buffer } from "./utils/bufferPromise.js"; import { LocaleRegistry, LocaleNotFoundException } from "./localeRegistry.js"; +import { normalizePath } from "vite"; +import { fileURLToPath } from "node:url"; /** * TypeSafe translations for Svelte & SvelteKit. @@ -40,16 +42,16 @@ export function t18sCore(pluginConfig) { registry.addEventListener("changed", async () => await regenerateDTS()); registry.addEventListener("locale_added", (e) => { - triggerHMREvent("t18s:createLocale", e.detail.locale); reporter.localeCreated(e.detail.locale); + triggerHMREvent("t18s:createLocale", e.detail.locale); }); registry.addEventListener("locale_removed", (e) => { - triggerHMREvent("t18s:removeLocale", e.detail.locale); reporter.localeDeleted(e.detail.locale); + triggerHMREvent("t18s:removeLocale", e.detail.locale); }); registry.addEventListener("locale_updated", (e) => { - triggerHMREvent("t18s:invalidateLocale", e.detail.locale); reporter.localeUpdated(e.detail.locale); + triggerHMREvent("t18s:invalidateLocale", e.detail.locale); }); /** Handles interactions with translation files */ @@ -240,6 +242,12 @@ export function t18sCore(pluginConfig) { id = cleanUrl(id); if (!id.startsWith(RESOLVED_VIRTUAL_MODULE_PREFIX)) return; + //Runtime entry + if (id === RESOLVED_VIRTUAL_MODULE_PREFIX + "/runtime") { + const runtimeEntryPath = getRuntimeEntryPath(); + return `export * from '${runtimeEntryPath}';`; + } + if (id === RESOLVED_VIRTUAL_MODULE_PREFIX) { const locales = registry.getLocales(); return generateMainModuleCode(locales, config.verbose); @@ -287,6 +295,15 @@ export function t18sCore(pluginConfig) { }; } +function getRuntimeEntryPath() { + const thisModulePath = normalizePath(dirname(fileURLToPath(import.meta.url))); + return thisModulePath.replace( + /\/t18s\/src\/core$/, + "/t18s/src/core/runtime/index.js", + ); +} + + /** * Remove hash and query parameters from a url. * @param {string} url diff --git a/src/core/runtime/T.svelte b/src/core/runtime/T.svelte new file mode 100644 index 0000000..51f7241 --- /dev/null +++ b/src/core/runtime/T.svelte @@ -0,0 +1,16 @@ + + + + +{$t(key, values)} diff --git a/src/core/runtime/index.js b/src/core/runtime/index.js new file mode 100644 index 0000000..b8c6c45 --- /dev/null +++ b/src/core/runtime/index.js @@ -0,0 +1 @@ +export { default as T } from "./T.svelte"; \ No newline at end of file diff --git a/src/index.js b/src/index.js index c0235cd..8747781 100644 --- a/src/index.js +++ b/src/index.js @@ -17,5 +17,5 @@ export function t18s(userConfig = {}) { /** @type {import("./types.js").t18sFullConfig} */ const pluginConfig = { ...DEFAULT_CONFIG, ...userConfig }; - return [t18sCore(pluginConfig), t18sToolkit()]; + return [t18sToolkit(), t18sCore(pluginConfig)]; } diff --git a/src/toolkit/index.js b/src/toolkit/index.js index 34675a5..921f148 100644 --- a/src/toolkit/index.js +++ b/src/toolkit/index.js @@ -18,7 +18,7 @@ export function t18sToolkit() { return { name: "t18s-toolkit", - apply: "serve", // only apply to dev server + //apply: "serve", // only apply to dev server enforce: "pre", configResolved(resolvedConfig) { @@ -26,7 +26,7 @@ export function t18sToolkit() { }, resolveId(id, _, options) { - if (options?.ssr) return; + //if (options?.ssr) return; if (id.startsWith(VIRTUAL_MODULE_PREFIX)) { return id.replace(VIRTUAL_MODULE_PREFIX, toolkitPath); @@ -34,7 +34,7 @@ export function t18sToolkit() { }, async load(id, options) { - if (options?.ssr) return; + //if (options?.ssr) return; if (id.startsWith(toolkitPath)) { // read file ourselves to avoid getting shut out by vites fs.allow check const file = cleanUrl(id);