diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index cd4bfaf18..db47a1bf9 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -7,7 +7,7 @@ on: paths-ignore: - "**/*.md" pull_request: - branches: + branches: - "**" release: types: @@ -29,10 +29,11 @@ jobs: id: node_modules_cache with: path: ./node_modules - key: ${{ runner.os }}-14-9-node_modules-${{ hashFiles('yarn.lock') }} + key: ${{ runner.os }}-14-9-9-node_modules-${{ hashFiles('yarn.lock') }} restore-keys: | - ${{ runner.os }}-14-9-node_modules- - ${{ runner.os }}-14-node_modules- + ${{ runner.os }}-14-9-9-node_modules- + ${{ runner.os }}-14-9- + ${{ runner.os }}-14- - name: Yarn offline cache if: steps.node_modules_cache.outputs.cache-hit != 'true' uses: actions/cache@v2 @@ -57,14 +58,16 @@ jobs: retention-days: 1 test: - runs-on: ubuntu-latest + runs-on: ${{ matrix.os }} needs: build strategy: matrix: + os: [ ubuntu-latest, macos-latest, windows-latest] node: ["12", "14", "16"] firebase: ["9"] + firebaseTools: ["9"] fail-fast: false - name: Test firebase@${{ matrix.firebase }} on Node.js ${{ matrix.node }} + name: Test firebase@${{ matrix.firebase }} firebase-tools@${{ matrix.firebaseTools }} on ${{ matrix.os }} Node.js ${{ matrix.node }} steps: - name: Checkout uses: actions/checkout@v2 @@ -78,10 +81,11 @@ jobs: uses: actions/cache@v2 with: path: ./node_modules - key: ${{ runner.os }}-${{ matrix.node }}-${{ matrix.firebase }}-node_modules-${{ hashFiles('yarn.lock') }} + key: ${{ runner.os }}-${{ matrix.node }}-${{ matrix.firebase }}-${{ matrix.firebaseTools }}-node_modules-${{ hashFiles('yarn.lock') }} restore-keys: | - ${{ runner.os }}-${{ matrix.node }}-${{ matrix.firebase }}-node_modules- - ${{ runner.os }}-${{ matrix.node }}-node_modules- + ${{ runner.os }}-${{ matrix.node }}-${{ matrix.firebase }}-${{ matrix.firebaseTools }}-node_modules- + ${{ runner.os }}-${{ matrix.node }}-${{ matrix.firebase }}- + ${{ runner.os }}-${{ matrix.node }}- - name: Yarn offline cache if: steps.node_modules_cache.outputs.cache-hit != 'true' uses: actions/cache@v2 @@ -95,6 +99,7 @@ jobs: yarn config set yarn-offline-mirror ~/.npm-packages-offline-cache yarn install --frozen-lockfile --prefer-offline --ignore-engines yarn add firebase@${{ matrix.firebase }} --prefer-offline --ignore-engines + yarn add firebase-tools@${{ matrix.firebaseTools }} --prefer-offline --ignore-engines - name: Firebase emulator cache uses: actions/cache@v2 with: @@ -107,6 +112,52 @@ jobs: mv angularfire-${{ github.run_id }} dist yarn test:node + contribute: + runs-on: ${{ matrix.os }} + name: Contribute ${{ matrix.os }} on Node.js ${{ matrix.node }} + strategy: + matrix: + os: [ ubuntu-latest, macos-latest, windows-latest ] + node: ["12", "14", "16"] + exclude: + # we build with this combination, safely skip + - os: ubuntu-latest + node: 14 + fail-fast: false + steps: + - name: Checkout + uses: actions/checkout@v2 + - name: Setup node + uses: actions/setup-node@v2-beta + with: + node-version: ${{ matrix.node }} + check-latest: true + - name: node_modules cache + uses: actions/cache@v2 + id: node_modules_cache + with: + path: ./node_modules + key: ${{ runner.os }}-${{ matrix.node }}-9-9-node_modules-${{ hashFiles('yarn.lock') }} + restore-keys: | + ${{ runner.os }}-${{ matrix.node }}-9-9-node_modules- + ${{ runner.os }}-${{ matrix.node }}-9- + ${{ runner.os }}-${{ matrix.node }}- + - name: Yarn offline cache + if: steps.node_modules_cache.outputs.cache-hit != 'true' + uses: actions/cache@v2 + with: + path: ~/.npm-packages-offline-cache + key: yarn-offline-${{ hashFiles('**/yarn.lock') }} + restore-keys: yarn-offline + - name: Install deps + if: steps.node_modules_cache.outputs.cache-hit != 'true' + run: | + yarn config set yarn-offline-mirror ~/.npm-packages-offline-cache + yarn install --frozen-lockfile --prefer-offline --ignore-engines + - name: Build + id: yarn-pack-dir + run: yarn build + headless: runs-on: ubuntu-latest needs: build @@ -129,10 +180,11 @@ jobs: uses: actions/cache@v2 with: path: ./node_modules - key: ${{ runner.os }}-14-${{ matrix.firebase }}-node_modules-${{ hashFiles('yarn.lock') }} + key: ${{ runner.os }}-14-${{ matrix.firebase }}-9-node_modules-${{ hashFiles('yarn.lock') }} restore-keys: | - ${{ runner.os }}-14-${{ matrix.firebase }}-node_modules- - ${{ runner.os }}-14-node_modules- + ${{ runner.os }}-14-${{ matrix.firebase }}-9-node_modules- + ${{ runner.os }}-14-9- + ${{ runner.os }}-14- - name: Yarn offline cache if: steps.node_modules_cache.outputs.cache-hit != 'true' uses: actions/cache@v2 @@ -179,10 +231,11 @@ jobs: # uses: actions/cache@v2 # with: # path: ./node_modules -# key: ${{ runner.os }}-14-${{ matrix.firebase }}-node_modules-${{ hashFiles('yarn.lock') }} +# key: ${{ runner.os }}-14-${{ matrix.firebase }}-9-node_modules-${{ hashFiles('yarn.lock') }} # restore-keys: | -# ${{ runner.os }}-14-${{ matrix.firebase }}-node_modules- -# ${{ runner.os }}-14-node_modules- +# ${{ runner.os }}-14-${{ matrix.firebase }}-9-node_modules- +# ${{ runner.os }}-14-${{ matrix.firebase }}- +# ${{ runner.os }}-14- # - name: Yarn offline cache # if: steps.node_modules_cache.outputs.cache-hit != 'true' # uses: actions/cache@v2 diff --git a/package.json b/package.json index 3907d786a..b147ce771 100644 --- a/package.json +++ b/package.json @@ -16,8 +16,8 @@ "test:typings": "node ./tools/run-typings-test.js", "test:build": "bash ./test/ng-build/build.sh", "test:all": "npm run test:node && npm run test:chrome-headless && npm run test:typings && npm run test:build", - "build": "rimraf dist; ttsc -p tsconfig.build.json; node --trace-warnings ./tools/build.js", - "build:jasmine": "tsc -p tsconfig.jasmine.json; cp ./dist/packages-dist/schematics/versions.json ./dist/out-tsc/jasmine/schematics", + "build": "rimraf dist && ttsc -p tsconfig.build.json && node --trace-warnings ./tools/build.js", + "build:jasmine": "tsc -p tsconfig.jasmine.json && cp ./dist/packages-dist/schematics/versions.json ./dist/out-tsc/jasmine/schematics", "changelog": "conventional-changelog -p angular -i CHANGELOG.md -s -r 1" }, "husky": { @@ -93,6 +93,7 @@ "codelyzer": "^6.0.0", "concurrently": "^2.2.0", "conventional-changelog-cli": "^1.2.0", + "cross-spawn": "^7.0.3", "file-loader": "^6.2.0", "firebase-functions-test": "^0.2.2", "globalthis": "^1.0.1", diff --git a/src/schematics/deploy/actions.jasmine.ts b/src/schematics/deploy/actions.jasmine.ts index 998328afa..d4202736b 100644 --- a/src/schematics/deploy/actions.jasmine.ts +++ b/src/schematics/deploy/actions.jasmine.ts @@ -2,6 +2,7 @@ import { JsonObject, logging } from '@angular-devkit/core'; import { BuilderContext, BuilderRun, ScheduleOptions, Target } from '@angular-devkit/architect'; import { BuildTarget, FirebaseDeployConfig, FirebaseTools, FSHost } from '../interfaces'; import deploy, { deployToFunction } from './actions'; +import { join } from 'path'; import 'jasmine'; let context: BuilderContext; @@ -169,8 +170,8 @@ describe('universal deployment', () => { const packageArgs = spy.calls.argsFor(0); const functionArgs = spy.calls.argsFor(1); - expect(packageArgs[0]).toBe('dist/package.json'); - expect(functionArgs[0]).toBe('dist/index.js'); + expect(packageArgs[0]).toBe(join('dist', 'package.json')); + expect(functionArgs[0]).toBe(join('dist', 'index.js')); }); it('should rename the index.html file in the nested dist', async () => { @@ -191,8 +192,8 @@ describe('universal deployment', () => { const packageArgs = spy.calls.argsFor(0); expect(packageArgs).toEqual([ - 'dist/dist/browser/index.html', - 'dist/dist/browser/index.original.html' + join('dist', 'dist', 'browser', 'index.html'), + join('dist', 'dist', 'browser', 'index.original.html') ]); }); diff --git a/src/schematics/ng-add.jasmine.ts b/src/schematics/ng-add.jasmine.ts index eca2b6814..4259272a3 100644 --- a/src/schematics/ng-add.jasmine.ts +++ b/src/schematics/ng-add.jasmine.ts @@ -1,6 +1,8 @@ import { Tree } from '@angular-devkit/schematics'; import { setupProject } from './ng-add'; import 'jasmine'; +import { join } from '@angular-devkit/core'; +import { join as pathJoin } from 'path'; const PROJECT_NAME = 'pie-ka-chu'; const PROJECT_ROOT = 'pirojok'; @@ -335,7 +337,7 @@ const projectAngularJson = `{ const universalFirebaseJson = { hosting: [{ target: 'pie-ka-chu', - public: 'dist/dist/ikachu', + public: pathJoin('dist', 'dist', 'ikachu'), ignore: [ '**/.*' ], diff --git a/tools/build.ts b/tools/build.ts index 2433e9150..4ccee3683 100644 --- a/tools/build.ts +++ b/tools/build.ts @@ -1,4 +1,4 @@ -import { spawn } from 'child_process'; +import { spawn } from 'cross-spawn'; import { copy, readFile, writeFile } from 'fs-extra'; import { prettySize } from 'pretty-size'; import { file as gzipSizeFile } from 'gzip-size'; @@ -44,7 +44,7 @@ function zoneWrapExports() { }); const zoneWrapped = toBeExported.filter(([, , zoneWrap]) => zoneWrap); const rawExport = toBeExported.filter(([, , zoneWrap]) => !zoneWrap); - await writeFile(`./src/${module}/${name}.ts`, `// DO NOT MODIFY, this file is autogenerated by tools/build.ts + await writeFile(join(process.cwd(), 'src', `${module}/${name}.ts`), `// DO NOT MODIFY, this file is autogenerated by tools/build.ts ${path.startsWith('firebase/') ? `export * from '${path}';\n` : ''}${ zoneWrapped.length > 0 ? `import { ɵzoneWrap } from '@angular/fire'; import { @@ -109,13 +109,14 @@ ${zoneWrapped.map(([importName, exportName]) => `export const ${exportName} = ɵ } function webpackFirestoreProtos() { + // TODO fix on windows return new Promise((resolve, reject) => { glob('./node_modules/@firebase/firestore/dist/src/protos/**/*.proto', {}, async (err, files) => { if (err) { reject(err); } const fileLoader = files.map(path => `require('file-loader?name=${path.replace('./node_modules/@firebase/firestore/dist/', '')}!${path.replace('./node_modules/', '../../')}');` ).join('\n'); - await writeFile('./dist/packages-dist/firestore-protos.js', fileLoader); + await writeFile(dest('firestore-protos.js'), fileLoader); resolve(); }); }); @@ -132,7 +133,7 @@ function proxyPolyfillCompat() { }; return Promise.all(Object.keys(defaultObject).map(module => - writeFile(`./src/${module}/base.ts`, `// DO NOT MODIFY, this file is autogenerated by tools/build.ts + writeFile(join(process.cwd(), 'src', `${module}/base.ts`), `// DO NOT MODIFY, this file is autogenerated by tools/build.ts // Export a null object with the same keys as firebase/${module}, so Proxy can work with proxy-polyfill in Internet Explorer export const proxyPolyfillCompat = { ${defaultObject[module].map(it => ` ${it}: null,`).join('\n')} @@ -197,6 +198,7 @@ async function fixImportForLazyModules() { const entries = Array.from(new Set(Object.values(packageJson).filter(v => typeof v === 'string' && v.endsWith('.js')))) as string[]; // TODO don't hardcode esm2015 here, perhaps we should scan all the entry directories // e.g, if ng-packagr starts building other non-flattened entries we'll lose the dynamic import + // TODO fix in Windows entries.push(`../${module.includes('/') ? '../' : ''}esm2015/${module}/public_api.js`); await Promise.all(entries.map(async path => { const source = (await readFile(dest(module, path))).toString(); @@ -233,55 +235,72 @@ function measureLibrary() { } async function buildDocs() { - // INVESTIGATE json to stdout rather than FS? - await Promise.all(MODULES.map(module => spawnPromise('npx', ['typedoc', `${module === 'core' ? './src' : `./src/${module}`}`, '--json', `./dist/typedocs/${module}.json`]))); - const entries = await Promise.all(MODULES.map(async (module) => { + try { + // INVESTIGATE json to stdout rather than FS? + await Promise.all( + MODULES.map(module => + spawnPromise('npx', ['typedoc', + `${module === 'core' ? + join(process.cwd(), 'src') : + join(process.cwd(), 'src', module)}`, + '--json', + join(process.cwd(), 'dist', 'typedocs', `${module}.json`) + ]))); + const entries = await Promise.all(MODULES.map(async (module) => { - const buffer = await readFile(`./dist/typedocs/${module}.json`); - const typedoc = JSON.parse(buffer.toString()); - if (!typedoc.children) { - console.error('typedoc fail', module); - } - // TODO infer the entryPoint from the package.json - const entryPoint = typedoc.children.find((c: any) => c.name === '"public_api"'); - const allChildren = [].concat(...typedoc.children.map(child => - // TODO chop out the working directory and filename - child.children ? child.children.map(c => ({ ...c, path: dirname(child.originalName.split(process.cwd())[1]) })) : [] - )); - return (entryPoint.children || []) - .filter(c => c.name[0] !== 'ɵ' && c.name[0] !== '_' /* private */) - .map(child => ({ ...allChildren.find(c => child.target === c.id) })) - .reduce((acc, child) => ({ ...acc, [encodeURIComponent(child.name)]: child }), {}); - })); - const root = await rootPackage; - const pipes = ['MonoTypeOperatorFunction', 'OperatorFunction', 'AuthPipe', 'UnaryFunction']; - const tocType = child => { - const decorators: string[] = child.decorators && child.decorators.map(d => d.name) || []; - if (decorators.includes('NgModule')) { - return 'NgModule'; - } else if (child.kindString === 'Type alias') { - return 'Type alias'; - } else if (child.kindString === 'Variable' && child.defaultValue && child.defaultValue.startsWith('new InjectionToken')) { - return 'InjectionToken'; - } else if (child.type) { - return pipes.includes(child.type.name) ? 'Pipe' : child.type.name; - } else if (child.signatures && child.signatures[0] && child.signatures[0].type && pipes.includes(child.signatures[0].type.name)) { - return 'Pipe'; - } else { - return child.kindString; - } - }; - const tableOfContents = entries.reduce((acc, entry, index) => - ({ - ...acc, [MODULES[index]]: { - name: ENTRY_NAMES[index], - exports: Object.keys(entry).reduce((acc, key) => ({ ...acc, [key]: tocType(entry[key]) }), {}) - } - }), - {} - ); - const afdoc = entries.reduce((acc, entry, index) => ({ ...acc, [MODULES[index]]: entry }), { table_of_contents: tableOfContents }); - return writeFile(`./api-${root.version}.json`, JSON.stringify(afdoc, null, 2)); + const buffer = await readFile(join(process.cwd(), 'dist', 'typedocs', `${module}.json`)); + const typedoc = JSON.parse(buffer.toString()); + if (!typedoc.children) { + console.error('typedoc fail', module); + } + // TODO infer the entryPoint from the package.json + const entryPoint = typedoc.children.find((c: any) => c.name === '"public_api"'); + const allChildren = [].concat(...typedoc.children.map(child => + // TODO chop out the working directory and filename + child.children ? + child.children.map(c => { + return { ...c, path: dirname(child.originalName.split(process.cwd())[1]) }; + }) : + [] + )); + return (entryPoint.children || []) + .filter(c => c.name[0] !== 'ɵ' && c.name[0] !== '_' /* private */) + .map(child => ({ ...allChildren.find(c => child.target === c.id) })) + .reduce((acc, child) => ({ ...acc, [encodeURIComponent(child.name)]: child }), {}); + })); + const root = await rootPackage; + const pipes = ['MonoTypeOperatorFunction', 'OperatorFunction', 'AuthPipe', 'UnaryFunction']; + const tocType = child => { + const decorators: string[] = child.decorators && child.decorators.map(d => d.name) || []; + if (decorators.includes('NgModule')) { + return 'NgModule'; + } else if (child.kindString === 'Type alias') { + return 'Type alias'; + } else if (child.kindString === 'Variable' && child.defaultValue && child.defaultValue.startsWith('new InjectionToken')) { + return 'InjectionToken'; + } else if (child.type) { + return pipes.includes(child.type.name) ? 'Pipe' : child.type.name; + } else if (child.signatures && child.signatures[0] && child.signatures[0].type && pipes.includes(child.signatures[0].type.name)) { + return 'Pipe'; + } else { + return child.kindString; + } + }; + const tableOfContents = entries.reduce((acc, entry, index) => + ({ + ...acc, [MODULES[index]]: { + name: ENTRY_NAMES[index], + exports: Object.keys(entry).reduce((acc, key) => ({ ...acc, [key]: tocType(entry[key]) }), {}) + } + }), + {} + ); + const afdoc = entries.reduce((acc, entry, index) => ({ ...acc, [MODULES[index]]: entry }), { table_of_contents: tableOfContents }); + return writeFile(join(process.cwd(), `api-${root.version}.json`), JSON.stringify(afdoc, null, 2)); + } catch (e) { + console.warn(e); + return Promise.resolve(); + } } Promise.all([ diff --git a/yarn.lock b/yarn.lock index cfe83585e..f307ab88a 100644 --- a/yarn.lock +++ b/yarn.lock @@ -4977,7 +4977,7 @@ cross-spawn@^6.0.0, cross-spawn@^6.0.5: shebang-command "^1.2.0" which "^1.2.9" -cross-spawn@^7.0.1: +cross-spawn@^7.0.1, cross-spawn@^7.0.3: version "7.0.3" resolved "https://registry.yarnpkg.com/cross-spawn/-/cross-spawn-7.0.3.tgz#f73a85b9d5d41d045551c177e2882d4ac85728a6" integrity sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==