diff --git a/packages/firestore/externs.json b/packages/firestore/externs.json
index a1746227316..a8e24a4a43d 100644
--- a/packages/firestore/externs.json
+++ b/packages/firestore/externs.json
@@ -37,6 +37,7 @@
"packages/firestore/src/local/indexeddb_schema.ts",
"packages/firestore/src/local/indexeddb_schema_legacy.ts",
"packages/firestore/src/local/shared_client_state_schema.ts",
+ "packages/firestore/src/util/rollup_info.ts",
"packages/firestore/src/util/testing_hooks.ts"
]
}
diff --git a/packages/firestore/lite/index.ts b/packages/firestore/lite/index.ts
index bec95b76124..5430df7a7f5 100644
--- a/packages/firestore/lite/index.ts
+++ b/packages/firestore/lite/index.ts
@@ -153,3 +153,5 @@ export { GeoPoint } from '../src/lite-api/geo_point';
export { Timestamp } from '../src/lite-api/timestamp';
export { FirestoreErrorCode, FirestoreError } from '../src/util/error';
+
+export { ROLLUP_BUNDLE_ID as _ROLLUP_BUNDLE_ID } from '../src/util/rollup_info';
diff --git a/packages/firestore/rollup.config.js b/packages/firestore/rollup.config.js
index 0f6a42205fd..674a49fd568 100644
--- a/packages/firestore/rollup.config.js
+++ b/packages/firestore/rollup.config.js
@@ -98,7 +98,10 @@ const allBuilds = [
},
plugins: [
...util.es2017ToEs5Plugins(/* mangled= */ false),
- replace(generateBuildTargetReplaceConfig('cjs', 2017))
+ replace(generateBuildTargetReplaceConfig('cjs', 2017)),
+ replace({
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'fstrbid_node_cjs'
+ })
],
external: util.resolveNodeExterns,
treeshake: {
@@ -115,7 +118,10 @@ const allBuilds = [
},
plugins: [
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('esm', 2017))
+ replace(generateBuildTargetReplaceConfig('esm', 2017)),
+ replace({
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'fstrbid_node_esm'
+ })
],
external: util.resolveNodeExterns,
treeshake: {
@@ -150,7 +156,10 @@ const allBuilds = [
],
plugins: [
...util.es2017ToEs5Plugins(/* mangled= */ true),
- replace(generateBuildTargetReplaceConfig('esm', 5))
+ replace(generateBuildTargetReplaceConfig('esm', 5)),
+ replace({
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'fstrbid_browser_esm5'
+ })
],
external: util.resolveBrowserExterns,
treeshake: {
@@ -169,7 +178,10 @@ const allBuilds = [
],
plugins: [
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('cjs', 2017))
+ replace(generateBuildTargetReplaceConfig('cjs', 2017)),
+ replace({
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'fstrbid_browser_cjs'
+ })
],
external: util.resolveBrowserExterns,
treeshake: {
@@ -188,7 +200,10 @@ const allBuilds = [
],
plugins: [
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('esm', 2017))
+ replace(generateBuildTargetReplaceConfig('esm', 2017)),
+ replace({
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'fstrbid_browser_esm'
+ })
],
external: util.resolveBrowserExterns,
treeshake: {
@@ -206,7 +221,10 @@ const allBuilds = [
plugins: [
alias(util.generateAliasConfig('rn')),
...browserPlugins(),
- replace(generateBuildTargetReplaceConfig('esm', 2017))
+ replace(generateBuildTargetReplaceConfig('esm', 2017)),
+ replace({
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'fstrbid_react_native'
+ })
],
external: util.resolveBrowserExterns,
treeshake: {
diff --git a/packages/firestore/rollup.config.lite.js b/packages/firestore/rollup.config.lite.js
index 6a4f55ffa56..53a56addca8 100644
--- a/packages/firestore/rollup.config.lite.js
+++ b/packages/firestore/rollup.config.lite.js
@@ -108,7 +108,8 @@ const allBuilds = [
}),
json(),
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('cjs', 5))
+ replace(generateBuildTargetReplaceConfig('cjs', 5)),
+ replace({ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'lite.node.cjs' })
],
external: util.resolveNodeExterns,
treeshake: {
@@ -125,7 +126,8 @@ const allBuilds = [
},
plugins: [
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('esm', 2017))
+ replace(generateBuildTargetReplaceConfig('esm', 2017)),
+ replace({ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'lite.node.mjs' })
],
external: util.resolveNodeExterns,
treeshake: {
@@ -167,7 +169,8 @@ const allBuilds = [
],
plugins: [
...util.es2017ToEs5Plugins(/* mangled= */ true),
- replace(generateBuildTargetReplaceConfig('esm', 5))
+ replace(generateBuildTargetReplaceConfig('esm', 5)),
+ replace({ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'lite.browser.esm5' })
],
external: util.resolveBrowserExterns,
treeshake: {
@@ -186,7 +189,8 @@ const allBuilds = [
],
plugins: [
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('cjs', 2017))
+ replace(generateBuildTargetReplaceConfig('cjs', 2017)),
+ replace({ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'lite.browser.cjs' })
],
external: util.resolveBrowserExterns,
treeshake: {
@@ -205,7 +209,8 @@ const allBuilds = [
],
plugins: [
sourcemaps(),
- replace(generateBuildTargetReplaceConfig('esm', 2017))
+ replace(generateBuildTargetReplaceConfig('esm', 2017)),
+ replace({ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'lite.browser.mjs' })
],
external: util.resolveBrowserExterns,
treeshake: {
@@ -225,8 +230,9 @@ const allBuilds = [
...browserPlugins(),
replace({
...generateBuildTargetReplaceConfig('esm', 2017),
- '__RUNTIME_ENV__': 'rn'
- })
+ '__RUNTIME_ENV__': 'rn',
+ '__FIRESTORE_ROLLUP_BUNDLE_ID__': 'lite.rn'
+ }),
],
external: util.resolveBrowserExterns,
treeshake: {
diff --git a/packages/firestore/rollup.shared.js b/packages/firestore/rollup.shared.js
index 92ec0e2ec9c..c90ca11b434 100644
--- a/packages/firestore/rollup.shared.js
+++ b/packages/firestore/rollup.shared.js
@@ -22,6 +22,7 @@ const typescriptPlugin = require('rollup-plugin-typescript2');
const typescript = require('typescript');
const { terser } = require('rollup-plugin-terser');
const path = require('path');
+const replace = require('rollup-plugin-replace');
const sourcemaps = require('rollup-plugin-sourcemaps');
const { renameInternals } = require('./scripts/rename-internals');
diff --git a/packages/firestore/src/api.ts b/packages/firestore/src/api.ts
index 510d95c8a89..dc011131370 100644
--- a/packages/firestore/src/api.ts
+++ b/packages/firestore/src/api.ts
@@ -237,3 +237,4 @@ export {
TestingHooks as _TestingHooks
} from './util/testing_hooks';
export { ExistenceFilterMismatchInfo as _TestingHooksExistenceFilterMismatchInfo } from './util/testing_hooks_spi';
+export { ROLLUP_BUNDLE_ID as _ROLLUP_BUNDLE_ID } from './util/rollup_info';
diff --git a/packages/firestore/src/util/rollup_info.ts b/packages/firestore/src/util/rollup_info.ts
new file mode 100644
index 00000000000..6c6cc21642a
--- /dev/null
+++ b/packages/firestore/src/util/rollup_info.ts
@@ -0,0 +1,32 @@
+/**
+ * @license
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ * The unique ID for this bundled version of the Firestore SDK as assigned by
+ * rollup.js at bundling time, or some undefined value if the SDK was not
+ * bundled with rollup.
+ *
+ * The ID can be used to determine which rollup.js plugins were used to bundle
+ * the SDK. See `../../rollup.shared.js` for details about how this value gets
+ * set by rollup at bundling time.
+ *
+ * The initial use case for this value is in tests that verify the bundled
+ * output of rollup.js.
+ *
+ * @internal
+ */
+export const ROLLUP_BUNDLE_ID = '__FIRESTORE_ROLLUP_BUNDLE_ID__';
diff --git a/packages/firestore/test/rollup/.idea/runConfigurations/mocha.xml b/packages/firestore/test/rollup/.idea/runConfigurations/mocha.xml
new file mode 100644
index 00000000000..18a513fca6c
--- /dev/null
+++ b/packages/firestore/test/rollup/.idea/runConfigurations/mocha.xml
@@ -0,0 +1,14 @@
+
+
+ project
+
+ $PROJECT_DIR$/node_modules/mocha
+ $PROJECT_DIR$
+ true
+ bdd
+
+ PATTERN
+ test/**/*.test.ts
+
+
+
\ No newline at end of file
diff --git a/packages/firestore/test/rollup/.mocharc.json b/packages/firestore/test/rollup/.mocharc.json
new file mode 100644
index 00000000000..3751de41484
--- /dev/null
+++ b/packages/firestore/test/rollup/.mocharc.json
@@ -0,0 +1,6 @@
+{
+ "extension": "ts",
+ "require": "ts-node/register",
+ "spec": "test/**/*.test.ts",
+ "timeout": 60000
+}
diff --git a/packages/firestore/test/rollup/README.md b/packages/firestore/test/rollup/README.md
new file mode 100644
index 00000000000..6c6f20bf543
--- /dev/null
+++ b/packages/firestore/test/rollup/README.md
@@ -0,0 +1,3 @@
+This directory contains tests for the rollup bundling process.
+It does not test the Firestore SDK itself, but rather the SDK bundles
+that get produced by rollup when commands like `yarn build` are run.
diff --git a/packages/firestore/test/rollup/bundle_id/.idea/runConfigurations/rollup.xml b/packages/firestore/test/rollup/bundle_id/.idea/runConfigurations/rollup.xml
new file mode 100644
index 00000000000..f18c22f5559
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/.idea/runConfigurations/rollup.xml
@@ -0,0 +1,12 @@
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/packages/firestore/test/rollup/bundle_id/README.md b/packages/firestore/test/rollup/bundle_id/README.md
new file mode 100644
index 00000000000..bcf89d9a88b
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/README.md
@@ -0,0 +1,18 @@
+This test verifies that the @firebase/firestore bundle that node.js chooses
+is the one that is expected.
+
+# Usage
+
+Run both of these commands:
+
+```
+node test.cjs
+```
+
+to test that the commonjs export from @firebase/firestore is used and
+
+```
+node test.mjs
+```
+
+to test that the ES modules export from @firebase/firestore is used.
diff --git a/packages/firestore/test/rollup/bundle_id/index.cjs b/packages/firestore/test/rollup/bundle_id/index.cjs
new file mode 100644
index 00000000000..87858885051
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/index.cjs
@@ -0,0 +1,21 @@
+/**
+ * @license
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+const { _ROLLUP_BUNDLE_ID } = require('@firebase/firestore');
+const { stdout } = require('node:process');
+
+stdout.write(_ROLLUP_BUNDLE_ID);
diff --git a/packages/firestore/test/rollup/bundle_id/index.mjs b/packages/firestore/test/rollup/bundle_id/index.mjs
new file mode 100644
index 00000000000..84b1adda228
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/index.mjs
@@ -0,0 +1,21 @@
+/**
+ * @license
+ * Copyright 2024 Google LLC
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import { _ROLLUP_BUNDLE_ID } from '@firebase/firestore';
+import { stdout } from 'node:process';
+
+stdout.write(_ROLLUP_BUNDLE_ID);
diff --git a/packages/firestore/test/rollup/bundle_id/package.json b/packages/firestore/test/rollup/bundle_id/package.json
new file mode 100644
index 00000000000..8154301d52f
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/package.json
@@ -0,0 +1,17 @@
+{
+ "scripts": {
+ "rollup": "rollup --config rollup.config.ts --configPlugin typescript2='{\"tsconfig\":\"tsconfig.rollup.json\",\"verbosity\":2}'"
+ },
+ "dependencies": {
+ "@firebase/firestore": "file:../../.."
+ },
+ "devDependencies": {
+ "@rollup/plugin-commonjs": "^25.0.7",
+ "@rollup/plugin-json": "^6.1.0",
+ "@rollup/plugin-node-resolve": "^15.2.3",
+ "@tsconfig/node16": "^16.1.1",
+ "rollup": "^4.9.5",
+ "rollup-plugin-typescript2": "^0.36.0",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/packages/firestore/test/rollup/bundle_id/rollup.config.ts b/packages/firestore/test/rollup/bundle_id/rollup.config.ts
new file mode 100644
index 00000000000..dc0c1893a6b
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/rollup.config.ts
@@ -0,0 +1,80 @@
+import type { RollupOptions } from 'rollup';
+import { nodeResolve } from '@rollup/plugin-node-resolve';
+import commonjs from '@rollup/plugin-commonjs';
+import json from '@rollup/plugin-json';
+
+function browserCommonJsRollupOptions(): RollupOptions {
+ return {
+ input: 'index.cjs',
+ output: {
+ file: 'dist/index.browser.cjs',
+ format: 'cjs'
+ },
+ plugins: [commonjs(), nodeResolve({ browser: true })]
+ };
+}
+
+function browserESModuleRollupOptions(): RollupOptions {
+ return {
+ input: 'index.mjs',
+ output: {
+ file: 'dist/index.browser.mjs',
+ format: 'es'
+ },
+ plugins: [commonjs(), nodeResolve({ browser: true })]
+ };
+}
+
+function browserEsm5RollupOptions(): RollupOptions {
+ return {
+ input: 'index.mjs',
+ output: {
+ file: 'dist/index.browser.esm5.mjs',
+ format: 'es'
+ },
+ plugins: [
+ commonjs(),
+ nodeResolve({ browser: true, exportConditions: ['esm5'] })
+ ]
+ };
+}
+
+function nodeCommonJsRollupOptions(): RollupOptions {
+ return {
+ input: 'index.cjs',
+ output: {
+ file: 'dist/index.node.cjs',
+ format: 'cjs'
+ },
+ plugins: [
+ json(),
+ commonjs(),
+ nodeResolve({ browser: false, exportConditions: ['node'] })
+ ]
+ };
+}
+
+function nodeESModuleRollupOptions(): RollupOptions {
+ return {
+ input: 'index.mjs',
+ output: {
+ file: 'dist/index.node.mjs',
+ format: 'es'
+ },
+ plugins: [
+ json(),
+ commonjs(),
+ nodeResolve({ browser: false, exportConditions: ['node'] })
+ ]
+ };
+}
+
+function* allRollupOptions(): Generator {
+ yield browserCommonJsRollupOptions();
+ yield browserESModuleRollupOptions();
+ yield browserEsm5RollupOptions();
+ yield nodeCommonJsRollupOptions();
+ yield nodeESModuleRollupOptions();
+}
+
+export default [...allRollupOptions()] satisfies RollupOptions[];
diff --git a/packages/firestore/test/rollup/bundle_id/tsconfig.rollup.json b/packages/firestore/test/rollup/bundle_id/tsconfig.rollup.json
new file mode 100644
index 00000000000..0b7ba9dd2a3
--- /dev/null
+++ b/packages/firestore/test/rollup/bundle_id/tsconfig.rollup.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@tsconfig/node16/tsconfig.json",
+ "files": ["rollup.config.ts"],
+ "compilerOptions": {
+ "outDir": "dist/rollup",
+ "module": "ES2020",
+ "moduleResolution": "Node"
+ }
+}
diff --git a/packages/firestore/test/rollup/package.json b/packages/firestore/test/rollup/package.json
new file mode 100644
index 00000000000..d4c37ef6b15
--- /dev/null
+++ b/packages/firestore/test/rollup/package.json
@@ -0,0 +1,16 @@
+{
+ "scripts": {
+ "build": "tsc",
+ "test": "mocha"
+ },
+ "devDependencies": {
+ "@tsconfig/node16": "^16.1.1",
+ "@types/chai": "^4.3.11",
+ "@types/mocha": "^10.0.6",
+ "@types/node": "^16.18.71",
+ "chai": "^4.2.0",
+ "mocha": "^10.2.0",
+ "ts-node": "^10.9.2",
+ "typescript": "^5.3.3"
+ }
+}
diff --git a/packages/firestore/test/rollup/test/bundle_id.test.ts b/packages/firestore/test/rollup/test/bundle_id.test.ts
new file mode 100644
index 00000000000..06cc0fbd6e2
--- /dev/null
+++ b/packages/firestore/test/rollup/test/bundle_id.test.ts
@@ -0,0 +1,88 @@
+import { expect } from 'chai';
+import { execFileSync } from 'node:child_process';
+
+// The path of the directory containing this file.
+const bundleIdDir = `${__dirname}/../bundle_id`;
+
+describe('bundle id in firestore bundles should match the expected', () => {
+ before(() => {
+ runNpmInstall(bundleIdDir);
+ runNpmRollup(bundleIdDir);
+ });
+
+ it('node.js cjs scripts should use node cjs bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/index.cjs`,
+ expectedBundleId: 'fstrbid_node_cjs'
+ });
+ });
+
+ it('node.js esm scripts should use node esm bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/index.mjs`,
+ expectedBundleId: 'fstrbid_node_esm'
+ });
+ });
+
+ it('rollup browser cjs scripts should use browser cjs bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/dist/index.browser.cjs`,
+ expectedBundleId: 'fstrbid_browser_cjs'
+ });
+ });
+
+ it('rollup browser esm scripts should use browser esm bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/dist/index.browser.mjs`,
+ expectedBundleId: 'fstrbid_browser_esm'
+ });
+ });
+
+ it('rollup browser esm5 scripts should use browser esm5 bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/dist/index.browser.esm5.mjs`,
+ expectedBundleId: 'fstrbid_browser_esm5'
+ });
+ });
+
+ it('rollup node cjs scripts should use node cjs bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/dist/index.node.cjs`,
+ expectedBundleId: 'fstrbid_node_cjs'
+ });
+ });
+
+ it('rollup node esm scripts should use node esm bundle', () => {
+ verifyBundleId({
+ jsScriptFile: `${bundleIdDir}/dist/index.node.mjs`,
+ expectedBundleId: 'fstrbid_node_esm'
+ });
+ });
+});
+
+function verifyBundleId(args: {
+ jsScriptFile: string;
+ expectedBundleId: string;
+}): void {
+ const rollupBundleId = runNodeScriptAndReturnOutput(args.jsScriptFile);
+ expect(rollupBundleId).to.equal(args.expectedBundleId);
+}
+
+function runNpmInstall(dir: string): void {
+ console.log(`npm install starting in directory: ${dir}`);
+ execFileSync('npm', ['install'], { cwd: dir, stdio: 'inherit' });
+ console.log(`npm install completed in directory: ${dir}`);
+}
+
+function runNpmRollup(dir: string): void {
+ console.log(`npm run rollup starting in directory: ${dir}`);
+ execFileSync('npm', ['run', 'rollup'], { cwd: dir, stdio: 'inherit' });
+ console.log(`npm run rollup completed in directory: ${dir}`);
+}
+
+function runNodeScriptAndReturnOutput(file: string): string {
+ console.log(`node script starting: ${file}`);
+ const output = execFileSync('node', [file], { encoding: 'utf8' });
+ console.log(`node script completed: ${file}`);
+ return output.trim();
+}
diff --git a/packages/firestore/test/rollup/tsconfig.json b/packages/firestore/test/rollup/tsconfig.json
new file mode 100644
index 00000000000..7b1dd4a2296
--- /dev/null
+++ b/packages/firestore/test/rollup/tsconfig.json
@@ -0,0 +1,9 @@
+{
+ "extends": "@tsconfig/node16/tsconfig.json",
+ "compilerOptions": {
+ "module": "commonjs",
+ "moduleResolution": "node",
+ "outDir": "dist"
+ },
+ "files": ["test.ts"]
+}
diff --git a/packages/firestore/tsconfig.json b/packages/firestore/tsconfig.json
index d53852fa79a..5682f61b58f 100644
--- a/packages/firestore/tsconfig.json
+++ b/packages/firestore/tsconfig.json
@@ -3,5 +3,5 @@
"compilerOptions": {
"outDir": "dist"
},
- "exclude": ["scripts/**/*", "dist/**/*"]
+ "exclude": ["scripts/**/*", "dist/**/*", "test/rollup/**/*"]
}