diff --git a/packages/cli/src/extract-experimental/buildIncludeDepsFilter.test.ts b/packages/cli/src/extract-experimental/buildIncludeDepsFilter.test.ts
new file mode 100644
index 000000000..377b415df
--- /dev/null
+++ b/packages/cli/src/extract-experimental/buildIncludeDepsFilter.test.ts
@@ -0,0 +1,13 @@
+import { buildIncludeDepsFilter } from "./buildIncludeDepsFilter"
+
+describe("buildExternalizeFilter", () => {
+ it("should not externalize packages from includeDeps", () => {
+ const isIncluded = buildIncludeDepsFilter(["package1", "package3/subpath"])
+
+ expect(isIncluded("package1")).toBeTruthy()
+ expect(isIncluded("package1/subpath")).toBeTruthy()
+ expect(isIncluded("package2")).toBeFalsy()
+ expect(isIncluded("package3/subpath")).toBeTruthy()
+ expect(isIncluded("package3/subpath/subpath")).toBeTruthy()
+ })
+})
diff --git a/packages/cli/src/extract-experimental/buildIncludeDepsFilter.ts b/packages/cli/src/extract-experimental/buildIncludeDepsFilter.ts
new file mode 100644
index 000000000..2547a2c36
--- /dev/null
+++ b/packages/cli/src/extract-experimental/buildIncludeDepsFilter.ts
@@ -0,0 +1,9 @@
+function createPackageRegExp(packageName: string) {
+ return new RegExp("^" + packageName + "(?:\\/.+)?")
+}
+
+export function buildIncludeDepsFilter(includeDeps: string[]) {
+ const include = includeDeps.map(createPackageRegExp)
+
+ return (id: string) => include.some((regExp) => regExp.test(id))
+}
diff --git a/packages/cli/src/extract-experimental/bundleSource.ts b/packages/cli/src/extract-experimental/bundleSource.ts
index 1f4a6f232..d8e2eebce 100644
--- a/packages/cli/src/extract-experimental/bundleSource.ts
+++ b/packages/cli/src/extract-experimental/bundleSource.ts
@@ -1,6 +1,7 @@
import { LinguiConfigNormalized } from "@lingui/conf"
import { BuildOptions } from "esbuild"
import { pluginLinguiMacro } from "./linguiEsbuildPlugin"
+import { buildIncludeDepsFilter } from "./buildIncludeDepsFilter"
function createExtRegExp(extensions: string[]) {
return new RegExp("\\.(?:" + extensions.join("|") + ")(?:\\?.*)?$")
@@ -49,11 +50,29 @@ export async function bundleSource(
sourcemap: "inline",
sourceRoot: outDir,
sourcesContent: false,
- packages: "external",
metafile: true,
plugins: [
pluginLinguiMacro({ linguiConfig }),
+ {
+ name: "externalize-deps",
+ setup(build) {
+ const shouldInclude = buildIncludeDepsFilter(config.includeDeps || [])
+
+ // considers all import paths that "look like" package imports in the original source code to be package imports.
+ // Specifically import paths that don't start with a path segment of / or . or .. are considered to be package imports.
+ // The only two exceptions to this rule are subpath imports (which start with a # character) and deps specified in the `includeDeps`
+ build.onResolve({ filter: /^[^.#/].*/ }, async (args) => {
+ if (shouldInclude(args.path)) {
+ return { external: false }
+ }
+
+ return {
+ external: true,
+ }
+ })
+ },
+ },
{
name: "externalize-files",
setup(build) {
diff --git a/packages/cli/test/extractor-experimental-template/expected/about.page.en.js b/packages/cli/test/extractor-experimental-template/expected/about.page.en.js
index 7707d434d..47e4c5d8c 100644
--- a/packages/cli/test/extractor-experimental-template/expected/about.page.en.js
+++ b/packages/cli/test/extractor-experimental-template/expected/about.page.en.js
@@ -1 +1 @@
-/*eslint-disable*/module.exports={messages:JSON.parse("{\"1TzdHc\":[\"aliased module message\"],\"8Pj7KC\":[\"JSX: about page message\"],\"LGGfGX\":[\"header message\"],\"u5PTM8\":[\"about page message\"]}")};
\ No newline at end of file
+/*eslint-disable*/module.exports={messages:JSON.parse("{\"1TzdHc\":[\"aliased module message\"],\"5iuJq+\":[\"subpath import module message\"],\"8Pj7KC\":[\"JSX: about page message\"],\"LGGfGX\":[\"header message\"],\"u5PTM8\":[\"about page message\"]}")};
\ No newline at end of file
diff --git a/packages/cli/test/extractor-experimental-template/expected/about.page.messages.pot b/packages/cli/test/extractor-experimental-template/expected/about.page.messages.pot
index 3b01c9627..460ad9a51 100644
--- a/packages/cli/test/extractor-experimental-template/expected/about.page.messages.pot
+++ b/packages/cli/test/extractor-experimental-template/expected/about.page.messages.pot
@@ -6,7 +6,7 @@ msgstr ""
"Content-Transfer-Encoding: 8bit\n"
"X-Generator: @lingui/cli\n"
-#: fixtures/pages/about.page.tsx:16
+#: fixtures/pages/about.page.tsx:19
msgid "about page message"
msgstr ""
@@ -18,6 +18,10 @@ msgstr ""
msgid "header message"
msgstr ""
-#: fixtures/pages/about.page.tsx:19
+#: fixtures/pages/about.page.tsx:22
msgid "JSX: about page message"
msgstr ""
+
+#: fixtures/components/subpath-import.ts:3
+msgid "subpath import module message"
+msgstr ""
diff --git a/packages/cli/test/extractor-experimental-template/expected/about.page.pl.js b/packages/cli/test/extractor-experimental-template/expected/about.page.pl.js
index 7707d434d..47e4c5d8c 100644
--- a/packages/cli/test/extractor-experimental-template/expected/about.page.pl.js
+++ b/packages/cli/test/extractor-experimental-template/expected/about.page.pl.js
@@ -1 +1 @@
-/*eslint-disable*/module.exports={messages:JSON.parse("{\"1TzdHc\":[\"aliased module message\"],\"8Pj7KC\":[\"JSX: about page message\"],\"LGGfGX\":[\"header message\"],\"u5PTM8\":[\"about page message\"]}")};
\ No newline at end of file
+/*eslint-disable*/module.exports={messages:JSON.parse("{\"1TzdHc\":[\"aliased module message\"],\"5iuJq+\":[\"subpath import module message\"],\"8Pj7KC\":[\"JSX: about page message\"],\"LGGfGX\":[\"header message\"],\"u5PTM8\":[\"about page message\"]}")};
\ No newline at end of file
diff --git a/packages/cli/test/extractor-experimental-template/fixtures/components/subpath-import.ts b/packages/cli/test/extractor-experimental-template/fixtures/components/subpath-import.ts
new file mode 100644
index 000000000..3a650575d
--- /dev/null
+++ b/packages/cli/test/extractor-experimental-template/fixtures/components/subpath-import.ts
@@ -0,0 +1,3 @@
+import { t } from "@lingui/core/macro"
+
+export const msg = t`subpath import module message`
diff --git a/packages/cli/test/extractor-experimental-template/fixtures/pages/about.page.tsx b/packages/cli/test/extractor-experimental-template/fixtures/pages/about.page.tsx
index 72630c149..01e536aad 100644
--- a/packages/cli/test/extractor-experimental-template/fixtures/pages/about.page.tsx
+++ b/packages/cli/test/extractor-experimental-template/fixtures/pages/about.page.tsx
@@ -13,10 +13,13 @@ import styles2 from "./styles.css?inline"
// should respect tsconfig path aliases
import { msg as msg2 } from "@alias"
+// should respect package.json subpath imports
+import { msg as msg3 } from "#subpath-dep"
+
const msg = t`about page message`
export default function Page() {
return JSX: about page message
}
-console.log(msg, headerMsg, bla, styles, styles2, msg2)
+console.log(msg, headerMsg, bla, styles, styles2, msg2, msg3)
diff --git a/packages/cli/test/extractor-experimental-template/lingui.config.ts b/packages/cli/test/extractor-experimental-template/lingui.config.ts
index 6110511ce..b465e51eb 100644
--- a/packages/cli/test/extractor-experimental-template/lingui.config.ts
+++ b/packages/cli/test/extractor-experimental-template/lingui.config.ts
@@ -9,6 +9,7 @@ export default defineConfig({
extractor: {
entries: ["/fixtures/pages/**/*.page.{ts,tsx}"],
output: "/actual/{entryName}.{locale}",
+ includeDeps: ["@alias"],
},
},
})
diff --git a/packages/cli/test/extractor-experimental-template/package.json b/packages/cli/test/extractor-experimental-template/package.json
index 3a655ff6a..972318aeb 100644
--- a/packages/cli/test/extractor-experimental-template/package.json
+++ b/packages/cli/test/extractor-experimental-template/package.json
@@ -2,6 +2,9 @@
"name": "extractor-experimental-template",
"version": "1.0.0",
"private": true,
+ "imports": {
+ "#subpath-dep": "./fixtures/components/subpath-import.ts"
+ },
"dependencies": {
"@lingui/core": "*",
"@lingui/react": "*",
diff --git a/packages/cli/test/extractor-experimental-template/tsconfig.json b/packages/cli/test/extractor-experimental-template/tsconfig.json
index 8c2301e65..84ab6623e 100644
--- a/packages/cli/test/extractor-experimental-template/tsconfig.json
+++ b/packages/cli/test/extractor-experimental-template/tsconfig.json
@@ -1,15 +1,12 @@
{
"compilerOptions": {
- "module": "commonjs",
- "target": "es5",
+ "module": "NodeNext",
+ "target": "ESNext",
+ "jsx": "preserve",
"baseUrl": "./",
"paths": {
- "@alias": [
- "./fixtures/components/aliased-module.ts"
- ]
+ "@alias": ["./fixtures/components/aliased-module.ts"]
}
},
- "exclude": [
- "node_modules"
- ]
+ "exclude": ["node_modules"]
}
diff --git a/packages/cli/test/index.test.ts b/packages/cli/test/index.test.ts
index ea16996e7..e9b1f87d5 100644
--- a/packages/cli/test/index.test.ts
+++ b/packages/cli/test/index.test.ts
@@ -84,18 +84,18 @@ describe("E2E Extractor Test", () => {
expect(replaceDuration(getConsoleMockCalls(console.log)))
.toMatchInlineSnapshot(`
- Done in ms
- Catalog statistics for actual/{locale}:
- ┌─────────────┬─────────────┬─────────┐
- │ Language │ Total count │ Missing │
- ├─────────────┼─────────────┼─────────┤
- │ en (source) │ 10 │ - │
- │ pl │ 10 │ 10 │
- └─────────────┴─────────────┴─────────┘
+ Done in ms
+ Catalog statistics for actual/{locale}:
+ ┌─────────────┬─────────────┬─────────┐
+ │ Language │ Total count │ Missing │
+ ├─────────────┼─────────────┼─────────┤
+ │ en (source) │ 10 │ - │
+ │ pl │ 10 │ 10 │
+ └─────────────┴─────────────┴─────────┘
- (Use "yarn extract" to update catalogs with new messages.)
- (Use "yarn compile" to compile catalogs for production. Alternatively, use bundler plugins: https://lingui.dev/ref/cli#compiling-catalogs-in-ci)
- `)
+ (Use "yarn extract" to update catalogs with new messages.)
+ (Use "yarn compile" to compile catalogs for production. Alternatively, use bundler plugins: https://lingui.dev/ref/cli#compiling-catalogs-in-ci)
+ `)
})
compareFolders(actualPath, expectedPath)
@@ -222,7 +222,7 @@ describe("E2E Extractor Test", () => {
Experimental features are not covered by semver, and may cause unexpected or broken application behavior. Use at your own risk.
Catalog statistics for fixtures/pages/about.page.tsx:
- 4 message(s) extracted
+ 5 message(s) extracted
Catalog statistics for fixtures/pages/index.page.ts:
1 message(s) extracted
@@ -455,18 +455,18 @@ describe("E2E Extractor Test", () => {
expect(replaceDuration(getConsoleMockCalls(console.log)))
.toMatchInlineSnapshot(`
- Done in ms
- Catalog statistics for actual/{locale}:
- ┌─────────────┬─────────────┬─────────┐
- │ Language │ Total count │ Missing │
- ├─────────────┼─────────────┼─────────┤
- │ en (source) │ 10 │ - │
- │ pl │ 10 │ 10 │
- └─────────────┴─────────────┴─────────┘
-
- (Use "yarn extract" to update catalogs with new messages.)
- (Use "yarn compile" to compile catalogs for production. Alternatively, use bundler plugins: https://lingui.dev/ref/cli#compiling-catalogs-in-ci)
- `)
+ Done in ms
+ Catalog statistics for actual/{locale}:
+ ┌─────────────┬─────────────┬─────────┐
+ │ Language │ Total count │ Missing │
+ ├─────────────┼─────────────┼─────────┤
+ │ en (source) │ 10 │ - │
+ │ pl │ 10 │ 10 │
+ └─────────────┴─────────────┴─────────┘
+
+ (Use "yarn extract" to update catalogs with new messages.)
+ (Use "yarn compile" to compile catalogs for production. Alternatively, use bundler plugins: https://lingui.dev/ref/cli#compiling-catalogs-in-ci)
+ `)
})
})
})
diff --git a/packages/conf/src/types.ts b/packages/conf/src/types.ts
index 07e26c2ad..8839fa849 100644
--- a/packages/conf/src/types.ts
+++ b/packages/conf/src/types.ts
@@ -138,25 +138,25 @@ export type ExperimentalExtractorOptions = {
entries: string[]
/**
- * Explicitly include some dependency for extraction.
- * For example, you can include all monorepo's packages as
- * ["@mycompany/"]
- */
- includeDeps?: string[]
-
- /**
- * By default all dependencies from package.json would be ecxluded from analyzing.
- * If something was not properly discovered you can add it here.
+ * List of package name patterns to include for extraction.
*
- * Note: it automatically matches also sub imports
+ * For example, to include all packages from your monorepo:
*
- * "next" would match "next" and "next/head"
+ * ["@mycompany"]
+ *
+ * By default, all imports that look like package imports are ignored.
+ * This means imports that do not start with `/`, `./`, `../`, or `#`
+ * (used for subpath imports). TypeScript path aliases are also ignored
+ * because they look like package imports.
+ *
+ * Add here the packages you want to include.
*/
- excludeDeps?: string[]
+ includeDeps?: string[]
/**
- * svg, jpg and other files which might be imported in application should be exluded from analysis.
- * By default extractor provides a comprehensive list of extensions. If you feel like somthing is missing in this list please fill an issue on GitHub
+ * svg, jpg and other files which might be imported in application should be excluded from analysis.
+ * By default, extractor provides a comprehensive list of extensions. If you feel like something
+ * is missing in this list please fill an issue on GitHub
*
* NOTE: changing this param will override default list of extensions.
*/