diff --git a/.eslintignore b/.eslintignore index 32e68a98cdc..6e9d3966136 100644 --- a/.eslintignore +++ b/.eslintignore @@ -1,3 +1,4 @@ test/*/samples/**/*.js !test/*/samples/**/_config.js test/leak/index.js +**/*.ts \ No newline at end of file diff --git a/cli/run/build.ts b/cli/run/build.ts index cc8b01d9985..ec2e5abe104 100644 --- a/cli/run/build.ts +++ b/cli/run/build.ts @@ -1,7 +1,7 @@ import ms from 'pretty-ms'; import tc from 'turbocolor'; import * as rollup from '../../src/node-entry'; -import { InputOptions, OutputAsset, OutputChunk, OutputOptions, RollupBuild, SourceMap } from '../../src/rollup/types'; +import { InputOptions, OutputOptions, RollupBuild, SourceMap } from '../../src/rollup/types'; import relativeId from '../../src/utils/relativeId'; import { handleError, stderr } from '../logging'; import SOURCEMAPPING_URL from '../sourceMappingUrl'; @@ -49,12 +49,12 @@ export default function build( return bundle.generate(output).then(({ output: outputs }) => { for (const file of outputs) { let source: string | Buffer; - if ((file as OutputAsset).isAsset) { - source = (file as OutputAsset).source; + if (file.type === 'asset') { + source = file.source; } else { - source = (file as OutputChunk).code; + source = file.code; if (output.sourcemap === 'inline') { - source += `\n//# ${SOURCEMAPPING_URL}=${((file as OutputChunk) + source += `\n//# ${SOURCEMAPPING_URL}=${(file .map as SourceMap).toUrl()}\n`; } } diff --git a/docs/02-javascript-api.md b/docs/02-javascript-api.md index 634638372f9..a209163c0de 100755 --- a/docs/02-javascript-api.md +++ b/docs/02-javascript-api.md @@ -25,12 +25,12 @@ async function build() { const { output } = await bundle.generate(outputOptions); for (const chunkOrAsset of output) { - if (chunkOrAsset.isAsset) { + if (chunkOrAsset.type === 'asset') { // For assets, this contains // { - // isAsset: true, // signifies that this is an asset // fileName: string, // the asset file name // source: string | Buffer // the asset source + // type: 'asset' // signifies that this is an asset // } console.log('Asset', chunkOrAsset); } else { @@ -54,6 +54,7 @@ async function build() { // }; // }, // name: string // the name of this chunk as used in naming patterns + // type: 'chunk', // signifies that this is a chunk // } console.log('Chunk', chunkOrAsset.modules); } diff --git a/docs/05-plugin-development.md b/docs/05-plugin-development.md index e42309a8ad3..3af70e13e55 100644 --- a/docs/05-plugin-development.md +++ b/docs/05-plugin-development.md @@ -123,8 +123,8 @@ Called at the end of `bundle.generate()` or immediately before the files are wri // AssetInfo { fileName: string, - isAsset: true, - source: string | Buffer + source: string | Buffer, + type: 'asset', } // ChunkInfo @@ -146,7 +146,8 @@ Called at the end of `bundle.generate()` or immediately before the files are wri originalLength: number }, }, - name: string + name: string, + type: 'chunk', } ``` diff --git a/src/rollup/index.ts b/src/rollup/index.ts index 4f0246b0f54..2960d43160f 100644 --- a/src/rollup/index.ts +++ b/src/rollup/index.ts @@ -168,7 +168,8 @@ function assignChunksToBundle( modules: chunk.renderedModules, get name() { return chunk.getChunkName(); - } + }, + type: 'chunk' } as OutputChunk; } return outputBundle as OutputBundle; @@ -301,7 +302,7 @@ export default async function rollup(rawInputOptions: GenericConfigObject): Prom let chunkCnt = 0; for (const fileName of Object.keys(bundle)) { const file = bundle[fileName]; - if ((file as OutputAsset).isAsset) continue; + if (file.type === 'asset') continue; chunkCnt++; if (chunkCnt > 1) break; } @@ -343,10 +344,10 @@ enum SortingFileType { } function getSortingFileType(file: OutputAsset | OutputChunk): SortingFileType { - if ((file as OutputAsset).isAsset) { + if (file.type === 'asset') { return SortingFileType.ASSET; } - if ((file as OutputChunk).isEntry) { + if (file.isEntry) { return SortingFileType.ENTRY_CHUNK; } return SortingFileType.SECONDARY_CHUNK; @@ -367,10 +368,6 @@ function createOutput(outputBundle: Record; let source: string | Buffer; - if (isOutputAsset(outputFile)) { + if (outputFile.type === 'asset') { source = outputFile.source; } else { source = outputFile.code; @@ -403,7 +400,7 @@ function writeOutputFile( .then(() => writeSourceMapPromise) .then( (): any => - !isOutputAsset(outputFile) && + outputFile.type === 'chunk' && graph.pluginDriver.hookSeq('onwrite', [ { bundle: build, diff --git a/src/rollup/types.d.ts b/src/rollup/types.d.ts index f24e305b4d7..8861c83dc12 100644 --- a/src/rollup/types.d.ts +++ b/src/rollup/types.d.ts @@ -314,8 +314,12 @@ export interface OutputBundle { [fileName: string]: OutputAsset | OutputChunk; } +export interface FilePlaceholder { + type: 'placeholder'; +} + export interface OutputBundleWithPlaceholders { - [fileName: string]: OutputAsset | OutputChunk | {}; + [fileName: string]: OutputAsset | OutputChunk | FilePlaceholder; } interface OnGenerateOptions extends OutputOptions { @@ -487,10 +491,11 @@ export interface SerializedTimings { } export interface OutputAsset { - code?: undefined; fileName: string; + /** @deprecated Accessing "isAsset" on files in the bundle is deprecated, please use "type === \'asset\'" instead */ isAsset: true; source: string | Buffer; + type: 'asset'; } export interface RenderedModule { @@ -520,6 +525,7 @@ export interface RenderedChunk extends PreRenderedChunk { export interface OutputChunk extends RenderedChunk { code: string; map?: SourceMap; + type: 'chunk'; } export interface SerializablePluginCache { diff --git a/src/utils/FileEmitter.ts b/src/utils/FileEmitter.ts index edd72dda3ba..0b29ae20748 100644 --- a/src/utils/FileEmitter.ts +++ b/src/utils/FileEmitter.ts @@ -2,7 +2,7 @@ import sha256 from 'hash.js/lib/hash/sha/256'; import Chunk from '../Chunk'; import Graph from '../Graph'; import Module from '../Module'; -import { OutputAsset, OutputBundleWithPlaceholders } from '../rollup/types'; +import { FilePlaceholder, OutputBundleWithPlaceholders } from '../rollup/types'; import { BuildPhase } from './buildPhase'; import { errAssetNotFinalisedForFileName, @@ -78,8 +78,8 @@ interface EmittedFile { type ConsumedFile = ConsumedChunk | ConsumedAsset; -export const FILE_PLACEHOLDER = { - isPlaceholder: true +export const FILE_PLACEHOLDER: FilePlaceholder = { + type: 'placeholder' }; function hasValidType( @@ -318,7 +318,20 @@ export class FileEmitter { // We must not modify the original assets to avoid interaction between outputs const assetWithFileName = { ...consumedFile, source, fileName }; this.filesByReferenceId.set(referenceId, assetWithFileName); - output.bundle[fileName] = { fileName, isAsset: true, source }; + const graph = this.graph; + output.bundle[fileName] = { + fileName, + get isAsset(): true { + graph.warnDeprecation( + 'Accessing "isAsset" on files in the bundle is deprecated, please use "type === \'asset\'" instead', + false + ); + + return true; + }, + source, + type: 'asset' + }; } private findExistingAssetFileNameWithSource( @@ -326,9 +339,9 @@ export class FileEmitter { source: string | Buffer ): string | null { for (const fileName of Object.keys(bundle)) { - const outputFile = bundle[fileName] as OutputAsset; + const outputFile = bundle[fileName]; if ( - outputFile.isAsset && + outputFile.type === 'asset' && (Buffer.isBuffer(source) && Buffer.isBuffer(outputFile.source) ? source.equals(outputFile.source) : source === outputFile.source) diff --git a/test/form/samples/deprecated/emit-asset/_config.js b/test/form/samples/deprecated/emit-asset/_config.js index 754262be404..d0a017cbd02 100644 --- a/test/form/samples/deprecated/emit-asset/_config.js +++ b/test/form/samples/deprecated/emit-asset/_config.js @@ -1,5 +1,6 @@ const fs = require('fs'); const path = require('path'); +const assert = require('assert'); module.exports = { description: 'supports emitting assets from plugin hooks', @@ -18,6 +19,20 @@ module.exports = { fs.readFileSync(id) )};`; } + }, + generateBundle(options, outputBundle) { + const keys = Object.keys(outputBundle); + assert.strictEqual(keys.length, 2); + assert.strictEqual(keys[0], 'assets/logo-25585ac1.svg'); + const asset = outputBundle[keys[0]]; + assert.strictEqual(asset.fileName, 'assets/logo-25585ac1.svg'); + assert.strictEqual(asset.isAsset, true); + assert.strictEqual(asset.type, 'asset'); + assert.ok( + asset.source.equals(fs.readFileSync(path.resolve(__dirname, 'logo.svg'))), + 'asset has correct source' + ); + assert.ok(keys[1].endsWith('.js'), `${keys[1]} ends with ".js"`); } } } diff --git a/test/form/samples/emit-asset-file/_config.js b/test/form/samples/emit-asset-file/_config.js index ae942ca0d5e..b9f1d83de22 100644 --- a/test/form/samples/emit-asset-file/_config.js +++ b/test/form/samples/emit-asset-file/_config.js @@ -26,7 +26,7 @@ module.exports = { assert.strictEqual(keys[0], 'assets/logo-25585ac1.svg'); const asset = outputBundle[keys[0]]; assert.strictEqual(asset.fileName, 'assets/logo-25585ac1.svg'); - assert.strictEqual(asset.isAsset, true); + assert.strictEqual(asset.type, 'asset'); assert.ok( asset.source.equals(fs.readFileSync(path.resolve(__dirname, 'logo.svg'))), 'asset has correct source'