Skip to content

Commit

Permalink
Merge pull request #3726 from parcel-bundler/wbinnssmith/inline-bundl…
Browse files Browse the repository at this point in the history
…e-urls

Encoded inline bundles
  • Loading branch information
Will Binns-Smith authored Nov 14, 2019
2 parents 1518f58 + 449490b commit bf85cd5
Show file tree
Hide file tree
Showing 54 changed files with 721 additions and 154 deletions.
16 changes: 16 additions & 0 deletions flow-libs/isbinaryfile.js.flow
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
// @flow

// Derived from the README for isbinaryfile, available at:
// https://github.com/gjtorikian/isBinaryFile#readme
// Which is licensed MIT.

declare module 'isbinaryfile' {
declare export function isBinaryFile(
buffer: Buffer,
bytesToRead?: number
): Promise<boolean>;
declare export function isBinaryFileSync(
buffer: Buffer,
bytesToRead?: number
): boolean;
}
7 changes: 4 additions & 3 deletions packages/bundlers/default/src/DefaultBundler.js
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,10 @@ export default new Bundler({
let resolution = bundleGraph.getDependencyResolution(dependency);

if (
dependency.isEntry ||
dependency.isAsync ||
resolution?.isIsolated
(dependency.isEntry && resolution) ||
(dependency.isAsync && resolution) ||
resolution?.isIsolated ||
resolution?.isInline
) {
let bundleGroup = bundleGraph.createBundleGroup(
dependency,
Expand Down
3 changes: 3 additions & 0 deletions packages/configs/default/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@
"bundler": "@parcel/bundler-default",
"transforms": {
"types:*.{ts,tsx}": ["@parcel/transformer-typescript-types"],
"bundle-text:*": ["@parcel/transformer-inline-string", "..."],
"data-url:*": ["@parcel/transformer-inline-string", "..."],
"*.{js,mjs,jsm,jsx,es6,ts,tsx}": [
"@parcel/transformer-babel",
"@parcel/transformer-js"
Expand Down Expand Up @@ -29,6 +31,7 @@
"node": ["@parcel/runtime-js"]
},
"optimizers": {
"data-url:*": ["...", "@parcel/optimizer-data-url"],
"*.css": ["@parcel/optimizer-cssnano"],
"*.js": ["@parcel/optimizer-terser"],
"*.html": ["@parcel/optimizer-htmlnano"]
Expand Down
2 changes: 2 additions & 0 deletions packages/configs/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
"@parcel/bundler-default": "^2.0.0-alpha.2.1",
"@parcel/namer-default": "^2.0.0-alpha.2.1",
"@parcel/optimizer-cssnano": "^2.0.0-alpha.2.1",
"@parcel/optimizer-data-url": "^2.0.0-alpha.2.1",
"@parcel/optimizer-terser": "^2.0.0-alpha.2.1",
"@parcel/optimizer-htmlnano": "^2.0.0-alpha.2.1",
"@parcel/packager-css": "^2.0.0-alpha.2.1",
Expand All @@ -34,6 +35,7 @@
"@parcel/transformer-coffeescript": "^2.0.0-alpha.2.1",
"@parcel/transformer-css": "^2.0.0-alpha.2.1",
"@parcel/transformer-html": "^2.0.0-alpha.2.1",
"@parcel/transformer-inline-string": "^2.0.0-alpha.2.1",
"@parcel/transformer-js": "^2.0.0-alpha.2.1",
"@parcel/transformer-json": "^2.0.0-alpha.2.1",
"@parcel/transformer-less": "^2.0.0-alpha.2.1",
Expand Down
4 changes: 4 additions & 0 deletions packages/configs/default/test/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ function collectConfigPackageReferences(

for (let value of Object.values(configSection)) {
if (typeof value === 'string') {
if (value === '...') {
continue;
}

references.add(value);
} else if (configSection != null && typeof configSection === 'object') {
collectConfigPackageReferences(value, references);
Expand Down
5 changes: 5 additions & 0 deletions packages/core/core/src/InternalAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ type AssetOptions = {|
outputHash?: string,
env: Environment,
meta?: Meta,
pipeline?: ?string,
stats: Stats,
symbols?: Map<Symbol, Symbol>,
sideEffects?: boolean,
Expand Down Expand Up @@ -72,6 +73,7 @@ export function createAsset(options: AssetOptions): Asset {
includedFiles: options.includedFiles || new Map(),
isSource: options.isSource,
outputHash: options.outputHash || '',
pipeline: options.pipeline,
env: options.env,
meta: options.meta || {},
stats: options.stats,
Expand Down Expand Up @@ -287,6 +289,9 @@ export default class InternalAsset {
: new Map(),
includedFiles: new Map(this.value.includedFiles),
meta: {...this.value.meta, ...result.meta},
pipeline:
result.pipeline ??
(this.value.type === result.type ? this.value.pipeline : null),
stats: {
time: 0,
size
Expand Down
92 changes: 6 additions & 86 deletions packages/core/core/src/PackagerRunner.js
Original file line number Diff line number Diff line change
Expand Up @@ -15,16 +15,10 @@ import type ParcelConfig from './ParcelConfig';
import type InternalBundleGraph from './BundleGraph';
import type {FileSystem, FileOptions} from '@parcel/fs';

import {
urlJoin,
md5FromObject,
md5FromString,
blobToStream
} from '@parcel/utils';
import {md5FromObject, md5FromString, blobToStream} from '@parcel/utils';
import {PluginLogger} from '@parcel/logger';
import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
import {Readable} from 'stream';
import invariant from 'assert';
import nullthrows from 'nullthrows';
import path from 'path';
import url from 'url';
Expand Down Expand Up @@ -233,7 +227,7 @@ export default class PackagerRunner {

let packager = await this.config.getPackager(bundle.filePath);
try {
let packaged = await packager.plugin.package({
return await packager.plugin.package({
bundle,
bundleGraph: new BundleGraph(bundleGraph, this.options),
getSourceMapReference: map => {
Expand All @@ -260,17 +254,6 @@ export default class PackagerRunner {
);
}
});

return {
contents:
typeof packaged.contents === 'string'
? replaceReferences(
packaged.contents,
generateDepToBundlePath(internalBundle, bundleGraph)
)
: packaged.contents,
map: packaged.map
};
} catch (e) {
throw new ThrowableDiagnostic({
diagnostic: errorToDiagnostic(e, packager.name)
Expand All @@ -285,7 +268,10 @@ export default class PackagerRunner {
map?: ?SourceMap
): Promise<BundleResult> {
let bundle = new NamedBundle(internalBundle, bundleGraph, this.options);
let optimizers = await this.config.getOptimizers(bundle.filePath);
let optimizers = await this.config.getOptimizers(
bundle.filePath,
internalBundle.pipeline
);
if (!optimizers.length) {
return {contents, map};
}
Expand Down Expand Up @@ -490,72 +476,6 @@ function writeFileStream(
});
}

/*
* Build a mapping from async, url dependency ids to web-friendly relative paths
* to their bundles. These will be relative to the current bundle if `publicUrl`
* is not provided. If `publicUrl` is provided, the paths will be joined to it.
*
* These are used to translate any placeholder dependency ids written during
* transformation back to a path that can be loaded in a browser (such as
* in a "raw" loader or any transformed dependencies referred to by url).
*/
function generateDepToBundlePath(
bundle: InternalBundle,
bundleGraph: InternalBundleGraph
): Map<string, FilePath> {
let depToBundlePath: Map<string, FilePath> = new Map();
bundleGraph.traverseBundle(bundle, node => {
if (node.type !== 'dependency') {
return;
}

let dep = node.value;
if (!dep.isURL || !dep.isAsync) {
return;
}

let [bundleGroupNode] = bundleGraph._graph.getNodesConnectedFrom(node);
invariant(bundleGroupNode && bundleGroupNode.type === 'bundle_group');

let [entryBundleNode] = bundleGraph._graph.getNodesConnectedFrom(
bundleGroupNode,
'bundle'
);
invariant(entryBundleNode && entryBundleNode.type === 'bundle');

let entryBundle = entryBundleNode.value;
depToBundlePath.set(
dep.id,
urlJoin(
nullthrows(entryBundle.target).publicUrl ?? '/',
nullthrows(entryBundle.name)
)
);
});

return depToBundlePath;
}

// replace references to url dependencies with relative paths to their
// corresponding bundles.
// TODO: This likely alters the length of the column in the source text.
// Update any sourcemaps accordingly.
function replaceReferences(
code: string,
depToBundlePath: Map<string, FilePath>
): string {
let output = code;
for (let [depId, replacement] of depToBundlePath) {
let split = output.split(depId);
if (split.length > 1) {
// the dependency id was found in the text. replace it.
output = split.join(replacement);
}
}

return output;
}

function getContentKey(cacheKey: string) {
return md5FromString(`${cacheKey}:content`);
}
Expand Down
22 changes: 12 additions & 10 deletions packages/core/core/src/ParcelConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -153,6 +153,12 @@ export default class ParcelConfig {
return this.loadPlugins<Validator>(names);
}

getNamedPipelines(): $ReadOnlyArray<string> {
return Object.keys(this.transforms)
.filter(glob => glob.includes(':'))
.map(glob => glob.split(':')[0]);
}
getTransformers(filePath: FilePath, pipeline?: ?string) {
return this.loadPlugins<Transformer>(
this.getTransformerNames(filePath, pipeline)
Expand Down Expand Up @@ -215,26 +221,22 @@ export default class ParcelConfig {
};
}
getOptimizerNames(filePath: FilePath): Array<string> {
let optimizers: ?Pipeline = this.matchGlobMapPipelines(
filePath,
this.optimizers
getOptimizerNames(filePath: FilePath, pipeline: ?string): Array<string> {
return (
this.matchGlobMapPipelines(filePath, this.optimizers, pipeline) ?? []
);
if (!optimizers) {
return [];
}
return optimizers;
}
getOptimizers(
filePath: FilePath
filePath: FilePath,
pipeline: ?string
): Promise<
Array<{|
name: string,
plugin: Optimizer
|}>
> {
let optimizers = this.getOptimizerNames(filePath);
let optimizers = this.getOptimizerNames(filePath, pipeline);
if (optimizers.length === 0) {
return Promise.resolve([]);
}
Expand Down
38 changes: 36 additions & 2 deletions packages/core/core/src/ResolverRunner.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
// @flow

import type {AssetRequestDesc, Dependency, ParcelOptions} from './types';
import type ParcelConfig from './ParcelConfig';

import {PluginLogger} from '@parcel/logger';
import ThrowableDiagnostic, {errorToDiagnostic} from '@parcel/diagnostic';
import path from 'path';
import type ParcelConfig from './ParcelConfig';
import URL from 'url';

import {report} from './ReporterRunner';
import PublicDependency from './public/Dependency';
import PluginOptions from './public/PluginOptions';
Expand Down Expand Up @@ -36,9 +38,41 @@ export default class ResolverRunner {

let resolvers = await this.config.getResolvers();

let pipeline;
let filePath;
let validPipelines = new Set(this.config.getNamedPipelines());
if (
// Don't consider absolute paths. Absolute paths are only supported for entries,
// and include e.g. `C:\` on Windows, conflicting with pipelines.
!path.isAbsolute(dependency.moduleSpecifier) &&
dependency.moduleSpecifier.includes(':')
) {
[pipeline, filePath] = dependency.moduleSpecifier.split(':');
if (!validPipelines.has(pipeline)) {
if (dep.isURL) {
// This may be a url protocol or scheme rather than a pipeline, such as
// `url('http://example.com/foo.png')`
return null;
} else {
throw new Error(`Unknown pipeline ${pipeline}.`);
}
}
} else {
filePath = dependency.moduleSpecifier;
}

if (dependency.isURL) {
let parsed = URL.parse(filePath);
if (typeof parsed.pathname !== 'string') {
throw new Error('Received URL without a pathname.');
}
filePath = decodeURIComponent(parsed.pathname);
}

for (let resolver of resolvers) {
try {
let result = await resolver.plugin.resolve({
filePath,
dependency: dep,
options: this.pluginOptions,
logger: new PluginLogger({origin: resolver.name})
Expand All @@ -54,7 +88,7 @@ export default class ResolverRunner {
sideEffects: result.sideEffects,
code: result.code,
env: dependency.env,
pipeline: dependency.pipeline
pipeline: pipeline ?? dependency.pipeline
};
}
} catch (e) {
Expand Down
4 changes: 3 additions & 1 deletion packages/core/core/src/Transformation.js
Original file line number Diff line number Diff line change
Expand Up @@ -111,7 +111,7 @@ export default class Transformation {
}

async loadAsset(): Promise<InternalAsset> {
let {filePath, env, code, sideEffects} = this.request;
let {filePath, env, code, pipeline, sideEffects} = this.request;
let {content, size, hash, isSource} = await summarizeRequest(
this.options.inputFS,
this.request
Expand All @@ -128,6 +128,7 @@ export default class Transformation {
isSource,
type: path.extname(filePath).slice(1),
hash,
pipeline,
env,
stats: {
time: 0,
Expand Down Expand Up @@ -594,6 +595,7 @@ function normalizeAssets(
env: result.env,
isIsolated: result.isIsolated,
isInline: result.isInline,
pipeline: internalAsset.value.pipeline,
meta: result.meta,
uniqueKey: internalAsset.value.uniqueKey
};
Expand Down
Loading

0 comments on commit bf85cd5

Please sign in to comment.