Skip to content

Commit f21776b

Browse files
Add tooling to verify dependency chain
1 parent 575b3fe commit f21776b

12 files changed

+395
-4
lines changed

package.json

+5-3
Original file line numberDiff line numberDiff line change
@@ -58,13 +58,17 @@
5858
"integration/*"
5959
],
6060
"devDependencies": {
61+
"@microsoft/api-documenter": "7.7.20",
62+
"@microsoft/api-extractor": "7.7.13",
6163
"@types/chai": "4.2.11",
6264
"@types/chai-as-promised": "7.1.2",
6365
"@types/long": "4.0.1",
6466
"@types/mocha": "7.0.2",
6567
"@types/node": "12.12.37",
6668
"@types/sinon": "9.0.0",
6769
"@types/sinon-chai": "3.2.4",
70+
"@types/tmp": "0.2.0",
71+
"@types/yargs": "15.0.4",
6872
"@typescript-eslint/eslint-plugin": "2.30.0",
6973
"@typescript-eslint/eslint-plugin-tslint": "2.30.0",
7074
"@typescript-eslint/parser": "2.30.0",
@@ -132,9 +136,7 @@
132136
"typescript": "3.8.3",
133137
"watch": "1.0.2",
134138
"webpack": "4.43.0",
135-
"yargs": "15.3.1",
136-
"@microsoft/api-extractor": "7.7.13",
137-
"@microsoft/api-documenter": "7.7.20"
139+
"yargs": "15.3.1"
138140
},
139141
"husky": {
140142
"hooks": {

packages/firestore/exp/index.d.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export function foo(): string;
19+
export function bar(): string;

packages/firestore/exp/index.node.ts

+18
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export { foo, bar } from './src/api/foobar';
+24
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
export function foo(): string {
19+
return bar();
20+
}
21+
22+
export function bar(): string {
23+
return 'bar';
24+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
{
2+
"foo": {
3+
"dependencies": [
4+
"bar",
5+
"foo"
6+
],
7+
"sizeInBytes": 67
8+
},
9+
"bar": {
10+
"dependencies": [
11+
"bar"
12+
],
13+
"sizeInBytes": 39
14+
}
15+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import { expect } from 'chai';
19+
20+
import { extractDependencies } from '../../../../../scripts/exp/extract-deps.helpers';
21+
22+
import * as dependencies from './dependencies.json';
23+
import * as pkg from '../../../package.json';
24+
import { forEach } from '../../../src/util/obj';
25+
26+
describe('Dependencies', () => {
27+
forEach(dependencies, (api, { dependencies }) => {
28+
it(api, () => {
29+
return extractDependencies(api, pkg.exp).then(extractedDependencies => {
30+
dependencies.sort();
31+
expect(extractedDependencies).to.have.members(dependencies);
32+
});
33+
});
34+
});
35+
});

packages/firestore/package.json

+7-1
Original file line numberDiff line numberDiff line change
@@ -11,11 +11,16 @@
1111
"build": "rollup -c rollup.config.es2017.js && rollup -c rollup.config.es5.js",
1212
"build:deps": "lerna run --scope @firebase/'{app,firestore}' --include-dependencies build",
1313
"build:console": "node tools/console.build.js",
14+
"build:exp": "rollup -c rollup.config.exp.js",
1415
"predev": "yarn prebuild",
1516
"dev": "rollup -c -w",
1617
"lint": "eslint -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'",
1718
"lint:fix": "eslint --fix -c .eslintrc.js '**/*.ts' --ignore-path '../../.gitignore'",
18-
"prettier": "prettier --write '*.ts' '*.js' 'src/**/*.js' 'test/**/*.js' 'src/**/*.ts' 'test/**/*.ts'",
19+
"prettier": "prettier --write '*.ts' '*.js' 'exp/**/*.ts' 'src/**/*.js' 'test/**/*.js' 'src/**/*.ts' 'test/**/*.ts'",
20+
"pregendeps:exp": "yarn build:exp",
21+
"gendeps:exp": "../../scripts/exp/extract-deps.sh --types ./exp/index.d.ts --bundle ./dist/exp/index.js --output ./exp/test/deps/dependencies.json",
22+
"pretest:exp": "yarn build:exp",
23+
"test:exp": "TS_NODE_CACHE=NO TS_NODE_COMPILER_OPTIONS='{\"module\":\"commonjs\"}' nyc --reporter lcovonly -- mocha 'exp/test/**/*.test.ts' --file exp/index.node.ts --config ../../config/mocharc.node.js",
1924
"test": "run-s lint test:all",
2025
"test:ci": "node ../../scripts/run_tests_in_ci.js",
2126
"test:all": "run-p test:browser test:travis test:minified",
@@ -34,6 +39,7 @@
3439
"browser": "dist/index.cjs.js",
3540
"module": "dist/index.esm.js",
3641
"esm2017": "dist/index.esm2017.js",
42+
"exp": "dist/exp/index.js",
3743
"license": "Apache-2.0",
3844
"files": [
3945
"dist",
+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import typescriptPlugin from 'rollup-plugin-typescript2';
19+
import typescript from 'typescript';
20+
21+
import { resolveNodeExterns } from './rollup.shared';
22+
23+
import pkg from './package.json';
24+
25+
const defaultPlugins = [
26+
typescriptPlugin({
27+
typescript,
28+
tsconfigOverride: {
29+
compilerOptions: {
30+
target: 'es2017'
31+
}
32+
},
33+
clean: true
34+
})
35+
];
36+
37+
const nodeBuilds = [
38+
{
39+
input: './exp/index.node.ts',
40+
output: {
41+
file: pkg.exp,
42+
format: 'es'
43+
},
44+
plugins: defaultPlugins,
45+
external: resolveNodeExterns,
46+
treeshake: {
47+
tryCatchDeoptimization: false
48+
}
49+
}
50+
];
51+
52+
export default [...nodeBuilds];

scripts/exp/extract-deps.helpers.ts

+111
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,111 @@
1+
/**
2+
* @license
3+
* Copyright 2020 Google LLC
4+
*
5+
* Licensed under the Apache License, Version 2.0 (the "License");
6+
* you may not use this file except in compliance with the License.
7+
* You may obtain a copy of the License at
8+
*
9+
* http://www.apache.org/licenses/LICENSE-2.0
10+
*
11+
* Unless required by applicable law or agreed to in writing, software
12+
* distributed under the License is distributed on an "AS IS" BASIS,
13+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
* See the License for the specific language governing permissions and
15+
* limitations under the License.
16+
*/
17+
18+
import * as tmp from 'tmp';
19+
import * as path from 'path';
20+
import * as fs from 'fs';
21+
import * as rollup from 'rollup';
22+
import * as terser from 'terser';
23+
import * as ts from 'typescript';
24+
25+
/** Contains the dependencies and the size of their code for a single export. */
26+
export type ExportData = { dependencies: string[]; sizeInBytes: number };
27+
28+
/**
29+
* This functions builds a simple JS app that only depends on the provided
30+
* export. It then uses Rollup to gather all top-level classes and functions
31+
* that that the export depends on.
32+
*
33+
* @param exportName The name of the export to verify
34+
* @param jsBundle The file name of the source bundle that contains the export
35+
* @return A list of dependencies for the given export
36+
*/
37+
export async function extractDependencies(
38+
exportName: string,
39+
jsBundle: string
40+
): Promise<string[]> {
41+
const { dependencies } = await extractDependenciesAndSize(
42+
exportName,
43+
jsBundle
44+
);
45+
return dependencies;
46+
}
47+
48+
/**
49+
* Helper for extractDependencies that extracts the dependencies and the size
50+
* of the minified build.
51+
*/
52+
export async function extractDependenciesAndSize(
53+
exportName: string,
54+
jsBundle: string
55+
): Promise<ExportData> {
56+
const input = tmp.fileSync().name + '.js';
57+
const output = tmp.fileSync().name + '.js';
58+
59+
// JavaScript content that exports a single API from the bundle
60+
const beforeContent = `export { ${exportName} } from '${path.resolve(
61+
jsBundle
62+
)}';`;
63+
fs.writeFileSync(input, beforeContent);
64+
65+
// Run Rollup on the JavaScript above to produce a tree-shaken build
66+
const bundle = await rollup.rollup({
67+
input,
68+
external: id => id.startsWith('@firebase/')
69+
});
70+
await bundle.write({ file: output, format: 'es' });
71+
72+
const dependencies = extractFunctionsAndClasses(output);
73+
dependencies.sort();
74+
75+
// Extract size of minified build
76+
const afterContent = fs.readFileSync(output, 'utf-8');
77+
const { code } = terser.minify(afterContent, {
78+
output: {
79+
comments: false
80+
},
81+
mangle: false,
82+
compress: false
83+
});
84+
85+
fs.unlinkSync(input);
86+
fs.unlinkSync(output);
87+
88+
return { dependencies, sizeInBytes: Buffer.byteLength(code!, 'utf-8') };
89+
}
90+
91+
/**
92+
* Extracts all top-level function and classes using the TypeScript compiler
93+
* API.
94+
*/
95+
export function extractFunctionsAndClasses(jsFile: string): string[] {
96+
const program = ts.createProgram([jsFile], { allowJs: true });
97+
const sourceFile = program.getSourceFile(jsFile);
98+
if (!sourceFile) {
99+
throw new Error('Failed to parse file: ' + jsFile);
100+
}
101+
102+
const exports: string[] = [];
103+
ts.forEachChild(sourceFile, node => {
104+
if (ts.isFunctionDeclaration(node)) {
105+
exports.push(node.name!.text);
106+
} else if (ts.isClassDeclaration(node)) {
107+
exports.push(node.name!.text);
108+
}
109+
});
110+
return exports;
111+
}

scripts/exp/extract-deps.sh

+31
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
#!/bin/bash
2+
3+
# Copyright 2020 Google Inc.
4+
#
5+
# Licensed under the Apache License, Version 2.0 (the "License");
6+
# you may not use this file except in compliance with the License.
7+
# You may obtain a copy of the License at
8+
#
9+
# http://www.apache.org/licenses/LICENSE-2.0
10+
#
11+
# Unless required by applicable law or agreed to in writing, software
12+
# distributed under the License is distributed on an "AS IS" BASIS,
13+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14+
# See the License for the specific language governing permissions and
15+
# limitations under the License.
16+
17+
# extract-deps --- Invokes extract-deps uaing TS_NODE
18+
19+
set -o nounset
20+
set -o errexit
21+
22+
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
23+
NPM_BIN_DIR="$(npm bin)"
24+
TSNODE="$NPM_BIN_DIR/ts-node"
25+
GENERATE_DEPS_JS="$DIR/extract-deps.ts"
26+
27+
export TS_NODE_CACHE=NO
28+
export TS_NODE_COMPILER_OPTIONS='{"module":"commonjs"}'
29+
export TS_NODE_PROJECT="$DIR/../../config/tsconfig.base.json"
30+
31+
$TSNODE $GENERATE_DEPS_JS "$@"

0 commit comments

Comments
 (0)