Skip to content

Commit

Permalink
feat(vite-plugin): report user-friendly error when macro used without…
Browse files Browse the repository at this point in the history
… transformation
  • Loading branch information
timofei-iatsenko committed Jun 28, 2023
1 parent dc82882 commit 7dfc5d6
Show file tree
Hide file tree
Showing 13 changed files with 444 additions and 60 deletions.
4 changes: 3 additions & 1 deletion packages/vite-plugin/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,9 @@
},
"devDependencies": {
"@lingui/format-json": "workspace:^",
"@lingui/macro": "workspace:^",
"unbuild": "^1.1.2",
"vite": "4.1.4"
"vite": "4.1.4",
"vite-plugin-babel-macros": "^1.0.6"
}
}
99 changes: 58 additions & 41 deletions packages/vite-plugin/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,64 +16,81 @@ type LinguiConfigOpts = {
skipValidation?: boolean
}

export function lingui(linguiConfig: LinguiConfigOpts = {}): Plugin {
export function lingui(linguiConfig: LinguiConfigOpts = {}): Plugin[] {
const config = getConfig(linguiConfig)

return {
name: "vite-plugin-lingui",

config: (config) => {
// https://github.com/lingui/js-lingui/issues/1464
config.optimizeDeps.exclude = config.optimizeDeps.exclude || []
config.optimizeDeps.exclude.push("@lingui/macro")
return [
{
name: "vite-plugin-lingui-report-macro-error",
enforce: "pre",
resolveId(id) {
if (id.includes("@lingui/macro")) {
throw new Error(
`The macro you imported from "@lingui/macro" is being executed outside the context of compilation. \n` +
`This indicates that you don't have the "babel-plugin-macros" or "@lingui/swc-plugin" configured correctly. ` +
`Please see the documentation for how to configure Vite with Lingui correctly: ` +
"https://lingui.dev/tutorials/setup-vite"
)
}
},
},
{
name: "vite-plugin-lingui",
config: (config) => {
// https://github.com/lingui/js-lingui/issues/1464
if (!config.optimizeDeps) {
config.optimizeDeps = {}
}
config.optimizeDeps.exclude = config.optimizeDeps.exclude || []
config.optimizeDeps.exclude.push("@lingui/macro")
},
async transform(src, id) {
if (fileRegex.test(id)) {
id = id.split("?")[0]

const catalogRelativePath = path.relative(config.rootDir, id)

const fileCatalog = getCatalogForFile(
catalogRelativePath,
await getCatalogs(config)
)

async transform(src, id) {
if (fileRegex.test(id)) {
id = id.split("?")[0]

const catalogRelativePath = path.relative(config.rootDir, id)

const fileCatalog = getCatalogForFile(
catalogRelativePath,
await getCatalogs(config)
)

if (!fileCatalog) {
throw new Error(
`Requested resource ${catalogRelativePath} is not matched to any of your catalogs paths specified in "lingui.config".
if (!fileCatalog) {
throw new Error(
`Requested resource ${catalogRelativePath} is not matched to any of your catalogs paths specified in "lingui.config".
Resource: ${id}
Your catalogs:
${config.catalogs.map((c) => c.path).join("\n")}
Please check that catalogs.path is filled properly.\n`
)
}
)
}

const { locale, catalog } = fileCatalog
const { locale, catalog } = fileCatalog

const dependency = await getCatalogDependentFiles(catalog, locale)
dependency.forEach((file) => this.addWatchFile(file))
const dependency = await getCatalogDependentFiles(catalog, locale)
dependency.forEach((file) => this.addWatchFile(file))

const messages = await catalog.getTranslations(locale, {
fallbackLocales: config.fallbackLocales,
sourceLocale: config.sourceLocale,
})
const messages = await catalog.getTranslations(locale, {
fallbackLocales: config.fallbackLocales,
sourceLocale: config.sourceLocale,
})

const compiled = createCompiledCatalog(locale, messages, {
strict: false,
namespace: "es",
pseudoLocale: config.pseudoLocale,
})
const compiled = createCompiledCatalog(locale, messages, {
strict: false,
namespace: "es",
pseudoLocale: config.pseudoLocale,
})

return {
code: compiled,
map: null, // provide source map if available
return {
code: compiled,
map: null, // provide source map if available
}
}
}
},
},
}
]
}

export default lingui
2 changes: 2 additions & 0 deletions packages/vite-plugin/test/__snapshots__/index.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`vite-plugin should not report error when macro correctly used 1`] = `Ola`;

exports[`vite-plugin should return compiled catalog 1`] = `
{
mVmaLu: [
Expand Down
55 changes: 39 additions & 16 deletions packages/vite-plugin/test/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,27 @@ describe("vite-plugin", () => {

expect((await mod.load()).messages).toMatchSnapshot()
})

skipOnWindows(
"should report error when macro used without a plugin",
async () => {
expect.assertions(1)
try {
await runVite(`no-macro-error/vite.config.ts`)
} catch (e) {
expect(e.stderr).toContain(
'The macro you imported from "@lingui/macro" is being executed outside the context of compilation.'
)
}
}
)
skipOnWindows(
"should not report error when macro correctly used",
async () => {
const mod = await runVite(`macro-usage/vite.config.ts`)
expect(await mod.load()).toMatchSnapshot()
}
)
})

async function runVite(configPath: string) {
Expand All @@ -36,27 +57,29 @@ async function runVite(configPath: string) {
` build -c ` +
path.resolve(__dirname, configPath) +
` --emptyOutDir --outDir ${outDir}`
await exec(command)
await exec(command, path.dirname(path.resolve(__dirname, configPath)))

return await import(path.resolve(outDir, "bundle.js"))
}

function exec(cmd: string) {
const _options = {
env: process.env,
}
function exec(cmd: string, cwd: string) {
return new Promise((resolve, reject) => {
_exec(cmd, _options, (error, stdout, stderr) => {
stdout = stdout.trim()
stderr = stderr.trim()

if (error === null) {
resolve({ stdout, stderr })
} else {
reject({ error, stdout, stderr })
console.error(stdout)
console.error(stderr)
_exec(
cmd,
{
env: process.env,
cwd,
},
(error, stdout, stderr) => {
stdout = stdout.trim()
stderr = stderr.trim()

if (error === null) {
resolve({ stdout, stderr })
} else {
reject({ error, stdout, stderr })
}
}
})
)
})
}
10 changes: 10 additions & 0 deletions packages/vite-plugin/test/macro-usage/.linguirc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"locales": ["en"],
"catalogs": [{
"path": "<rootDir>/locale/{locale}"
}],
"fallbackLocales": {
"default": "en"
},
"format": "po"
}
5 changes: 5 additions & 0 deletions packages/vite-plugin/test/macro-usage/entrypoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { t } from "@lingui/macro"

export async function load() {
return t`Ola`
}
5 changes: 5 additions & 0 deletions packages/vite-plugin/test/macro-usage/locale/en.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
msgid "Hello World"
msgstr "Hello World"

msgid "My name is {name}"
msgstr "My name is {name}"
11 changes: 11 additions & 0 deletions packages/vite-plugin/test/macro-usage/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
import { createDefaultViteConfig } from "../default-vite.config"
import { UserConfig } from "vite"
import macrosPlugin from "vite-plugin-babel-macros"

const config: UserConfig = {
...createDefaultViteConfig(__dirname),
}

config.plugins.push(macrosPlugin())

export default config
10 changes: 10 additions & 0 deletions packages/vite-plugin/test/no-macro-error/.linguirc
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
{
"locales": ["en"],
"catalogs": [{
"path": "<rootDir>/locale/{locale}"
}],
"fallbackLocales": {
"default": "en"
},
"format": "po"
}
5 changes: 5 additions & 0 deletions packages/vite-plugin/test/no-macro-error/entrypoint.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { t } from "@lingui/macro"

export async function load() {
return t`Ola`
}
5 changes: 5 additions & 0 deletions packages/vite-plugin/test/no-macro-error/locale/en.po
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
msgid "Hello World"
msgstr "Hello World"

msgid "My name is {name}"
msgstr "My name is {name}"
3 changes: 3 additions & 0 deletions packages/vite-plugin/test/no-macro-error/vite.config.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { createDefaultViteConfig } from "../default-vite.config"

export default createDefaultViteConfig(__dirname)
Loading

0 comments on commit 7dfc5d6

Please sign in to comment.