Skip to content

Commit 08ec5ec

Browse files
committed
Cleanup, add __firebaseAppName to query
1 parent 8b1a9e7 commit 08ec5ec

File tree

8 files changed

+54
-33
lines changed

8 files changed

+54
-33
lines changed

src/client/auth/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,12 +12,13 @@ import type { FirebaseApp } from 'firebase/app';
1212
import { ID_TOKEN_MAX_AGE } from '../../constants';
1313

1414
let alreadySetup = false;
15-
let lastPostedIdToken: string|undefined;
15+
let lastPostedIdToken: string|undefined|null = null;
1616

1717
const mintCookie = async (user: User|null) => {
1818
const idTokenResult = user && await user.getIdTokenResult();
1919
const idTokenAge = idTokenResult && (new Date().getTime() - Date.parse(idTokenResult.issuedAtTime)) / 1_000;
2020
if (idTokenAge && idTokenAge > ID_TOKEN_MAX_AGE) return;
21+
// Specifically trip null => undefined when logged out, to delete any existing cookie
2122
const idToken = idTokenResult?.token;
2223
if (lastPostedIdToken === idToken) return;
2324
lastPostedIdToken = idToken;

src/constants.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,4 @@ export const LRU_MAX_INSTANCES = 100;
88
export const LRU_TTL = 1_000 * 60 * 5;
99
export const ID_TOKEN_MAX_AGE = 5 * 60;
1010
export const COOKIE_MAX_AGE = 60 * 60 * 24 * 5 * 1_000;
11+
export const MIN_FIREBASE_SDK_FOR_AUTH = '9.8.0';

src/frameworks/angular/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ export const build = async (config: DeployConfig | Required<DeployConfig>, getPr
3030
project = angularJson.defaultProject;
3131
if (!project) throw `angular.json missing defaultProject`;
3232
}
33+
3334
// TODO if there are multiple projects warn
3435
const workspaceProject = workspace.projects.get(project);
3536
if (!workspaceProject) throw `No project ${project} found.`;

src/frameworks/next.js/index.ts

Lines changed: 18 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -13,15 +13,15 @@
1313
// limitations under the License.
1414

1515
import { readFile, mkdir, copyFile, stat } from 'fs/promises';
16-
import { dirname, extname, join, relative, resolve, sep } from 'path';
16+
import { dirname, extname, join } from 'path';
1717
import type { Header, Rewrite, Redirect } from 'next/dist/lib/load-custom-routes';
1818
import type { NextConfig } from 'next';
1919
import nextBuild from 'next/dist/build';
20+
import nextExport from 'next/dist/export';
2021
import { copy } from 'fs-extra';
21-
import * as webpack from 'webpack';
22-
import { lt } from 'semver';
22+
import { trace } from 'next/dist/trace';
2323

24-
import { DeployConfig, PathFactory, exec, findDependency } from '../../utils';
24+
import { DeployConfig, PathFactory, exec, findDependency, getWebpackPlugin } from '../../utils';
2525

2626
export const build = async (config: DeployConfig | Required<DeployConfig>, getProjectPath: PathFactory) => {
2727

@@ -44,46 +44,39 @@ export const build = async (config: DeployConfig | Required<DeployConfig>, getPr
4444
webpack: (config, context) => {
4545
let newConfig = config;
4646
if (nextConfig.webpack) newConfig = nextConfig.webpack(config, context);
47-
const plugin = new webpack.NormalModuleReplacementPlugin(/^firebase\/(auth)$/, (resource: any) => {
48-
// Don't allow firebase-frameworks to recurse
49-
const frameworksRoot = resolve(`${dirname(require.resolve('../..'))}${sep}..`);
50-
if (resource.context.startsWith(frameworksRoot)) return;
51-
// Don't mutate their node_modules
52-
if (relative(getProjectPath(), resource.context).startsWith(`node_modules${sep}`)) return;
53-
const client = resource.request.split('firebase/')[1];
54-
// auth requires beforeAuthStateChanged, released in 9.7.1
55-
if (client === 'auth' && lt(firebaseDependency.version, '9.7.1')) return;
56-
resource.request = require.resolve(`../../client/${client}`);
57-
});
47+
const plugin = getWebpackPlugin(getProjectPath());
5848
newConfig.plugins ||= [];
5949
newConfig.plugins.push(plugin);
6050
return newConfig;
6151
}
6252
}
6353
}
6454

65-
await nextBuild(getProjectPath(), overrideConfig as any, false, false, true);
66-
// TODO be a bit smarter about this
67-
await exec(`${getProjectPath('node_modules', '.bin', 'next')} export`, { cwd: getProjectPath() }).catch(() => {});
6855

6956
// SEMVER these defaults are only needed for Next 11
7057
const { distDir='.next', basePath='' } = nextConfig;
7158

7259
const deployPath = (...args: string[]) => config.dist ? join(config.dist, ...args) : getProjectPath('.deploy', ...args);
7360
const getHostingPath = (...args: string[]) => deployPath('hosting', ...basePath.split('/'), ...args);
7461

75-
await mkdir(getHostingPath('_next', 'static'), { recursive: true });
62+
await nextBuild(getProjectPath(), overrideConfig as any, false, false, true);
63+
64+
await nextExport(
65+
getProjectPath(),
66+
{ silent: true, outdir: getHostingPath() },
67+
trace('next-export-cli')
68+
).catch(() => {
69+
console.warn('\nUnable to export the app, treating as SSR.\n\n');
70+
});
7671

7772
let usingCloudFunctions = !!config.function;
7873
const asyncSteps: Array<Promise<any>> = [];
7974

8075
const exportDetailJson = await readFile(getProjectPath(distDir, 'export-detail.json')).then(it => JSON.parse(it.toString()), () => { success: false });
8176
if (exportDetailJson.success) {
8277
usingCloudFunctions = false;
83-
asyncSteps.push(
84-
copy(exportDetailJson.outDirectory, getHostingPath())
85-
);
8678
} else {
79+
await mkdir(getHostingPath('_next', 'static'), { recursive: true });
8780
await copy(getProjectPath('public'), getHostingPath());
8881
await copy(getProjectPath(distDir, 'static'), getHostingPath('_next', 'static'));
8982

@@ -152,11 +145,11 @@ export const build = async (config: DeployConfig | Required<DeployConfig>, getPr
152145
return { source, destination };
153146
}).filter(it => it);
154147

148+
// TODO use this better detection for usesFirebaseConfig
155149
return { usingCloudFunctions, usesFirebaseConfig, headers, redirects, rewrites, framework: 'next.js', packageJson, bootstrapScript: null };
156150
}
157151

158-
159-
export type Manifest = {
152+
type Manifest = {
160153
distDir?: string,
161154
basePath?: string,
162155
headers?: (Header & { regex: string})[],
@@ -166,4 +159,4 @@ export type Manifest = {
166159
afterFiles?: (Rewrite & { regex: string})[],
167160
fallback?: (Rewrite & { regex: string})[],
168161
},
169-
};
162+
};

src/frameworks/next.js/server.ts

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,17 @@
11
import { parse } from 'url';
22
import next from 'next';
3-
import type { Request } from 'firebase-functions/v2/https';
3+
import type { Request } from '../../server';
44
import type { Response } from 'express';
55

66
const nextApp = next({ dev: false, dir: process.cwd() });
77
const nextAppPrepare = nextApp.prepare();
88

99
export const handle = async (req: Request, res: Response) => {
10-
const parsedUrl = parse(req.url, true);
10+
const fauxHost = 'http://firebase-frameworks';
11+
const url = new URL(`${fauxHost}${req.url}`);
12+
url.searchParams.delete('__firebaseAppName');
13+
if (req.firebaseApp) url.searchParams.set('__firebaseAppName', req.firebaseApp.name);
14+
const parsedUrl = parse(url.toString().slice(fauxHost.length), true);
1115
await nextAppPrepare;
1216
nextApp.getRequestHandler()(req, res, parsedUrl);
13-
};
17+
};

src/server/index.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { onRequest } from 'firebase-functions/v2/https';
2+
export { Request } from './firebase-aware';
23

34
const { HTTPS_OPTIONS, FRAMEWORK } = require(`${process.cwd()}/settings`);
45
const { handle } = require(`../frameworks/${FRAMEWORK}/server`);

src/utils.ts

Lines changed: 19 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,10 @@
1313
// limitations under the License.
1414

1515
import { exec as execCallback, spawn as spawnCallback, ExecOptions, SpawnOptionsWithoutStdio, spawnSync } from 'child_process';
16-
import { join } from 'path';
16+
import { dirname, join, relative, resolve, sep } from 'path';
17+
import { lt } from 'semver';
18+
import { NormalModuleReplacementPlugin } from 'webpack';
19+
import { MIN_FIREBASE_SDK_FOR_AUTH } from './constants';
1720

1821
export const exec = (command: string, options: ExecOptions={}) => new Promise((resolve, reject) =>
1922
execCallback(command, options, (error, stdout) => {
@@ -75,3 +78,18 @@ export const findDependency = (name: string, cwd=process.cwd()) => {
7578
}
7679
return search(name, json.dependencies);
7780
}
81+
82+
export const getWebpackPlugin = (cwd: string) => new NormalModuleReplacementPlugin(/^firebase\/(auth)$/, (resource: any) => {
83+
// Don't allow firebase-frameworks to recurse
84+
const frameworksRoot = resolve(`${dirname(require.resolve('.'))}${sep}..`);
85+
if (resource.context.startsWith(frameworksRoot)) return;
86+
// Don't mutate their node_modules
87+
if (relative(cwd, resource.context).startsWith(`node_modules${sep}`)) return;
88+
const client = resource.request.split('firebase/')[1];
89+
const firebaseDependency = findDependency('firebase', cwd);
90+
// auth requires beforeAuthStateChanged, released in 9.8.0
91+
if (client === 'auth' && lt(firebaseDependency.version, MIN_FIREBASE_SDK_FOR_AUTH)) return;
92+
// TODO log to the firebase.log
93+
console.log(`Substituting import of '${resource.request}' with 'firebase-frameworks/client/${client}' in ${relative(cwd, resource.context)}.`);
94+
resource.request = require.resolve(`./client/${client}`);
95+
});

tools/build.ts

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,8 @@ const LOCAL_NODE_MODULES = [
77
'@angular-devkit/architect/node',
88
'@angular-devkit/architect',
99
'next/dist/build',
10+
'next/dist/export',
11+
'next/dist/trace',
1012
'nuxt',
1113
'@nuxt/kit/dist/index.mjs',
1214
'webpack'
@@ -30,19 +32,19 @@ const main = async () => {
3032
});
3133

3234
await replaceInFile({
33-
files: 'dist/frameworks/**/index.js',
35+
files: 'dist/**/*',
3436
from: LOCAL_NODE_MODULES.map(mod => `require("${mod}")`),
3537
to: LOCAL_NODE_MODULES.map(mod => `require(\`\${process.cwd()}/node_modules/${mod}\`)`),
3638
});
3739

3840
await replaceInFile({
39-
files: 'dist/frameworks/**/index.js',
41+
files: 'dist/**/*',
4042
from: LOCAL_NODE_MODULES.map(mod => `require('${mod}')`),
4143
to: LOCAL_NODE_MODULES.map(mod => `require(\`\${process.cwd()}/node_modules/${mod}\`)`),
4244
});
4345

4446
await replaceInFile({
45-
files: 'dist/frameworks/**/index.js',
47+
files: 'dist/**/*',
4648
from: LOCAL_NODE_MODULES.map(mod => `import('${mod}')`),
4749
to: LOCAL_NODE_MODULES.map(mod => `import(\`\${process.cwd()}/node_modules/${mod}\`)`),
4850
});

0 commit comments

Comments
 (0)