From c008f5f328c84bc1d331de9acb14a7bca928ac28 Mon Sep 17 00:00:00 2001 From: zzheng Date: Tue, 15 Aug 2023 12:10:45 +1000 Subject: [PATCH 1/5] expose transform and resolveapi from parcel --- packages/core/core/src/Parcel.js | 87 ++++++++++++++++++- packages/core/core/test/Parcel.test.js | 53 +++++++++++ .../core/core/test/fixtures/parcel/index.js | 1 + .../core/core/test/fixtures/parcel/other.js | 3 + packages/core/types/index.js | 22 +++++ .../js/core/src/dependency_collector.rs | 29 ++++--- packages/transformers/js/core/src/lib.rs | 1 + packages/transformers/js/src/JSTransformer.js | 1 + 8 files changed, 184 insertions(+), 13 deletions(-) create mode 100644 packages/core/core/test/fixtures/parcel/other.js diff --git a/packages/core/core/src/Parcel.js b/packages/core/core/src/Parcel.js index 9adff5dccd7..53c9965acfc 100644 --- a/packages/core/core/src/Parcel.js +++ b/packages/core/core/src/Parcel.js @@ -1,12 +1,17 @@ // @flow strict-local import type { + Asset, AsyncSubscription, BuildEvent, BuildSuccessEvent, InitialParcelOptions, PackagedBundle as IPackagedBundle, + ParcelTransformOptions, + ParcelResolveOptions, + ParcelResolveResult, } from '@parcel/types'; +import path from 'path'; import type {ParcelOptions} from './types'; // eslint-disable-next-line no-unused-vars import type {FarmOptions, SharedReference} from '@parcel/workers'; @@ -37,10 +42,18 @@ import RequestTracker, { } from './RequestTracker'; import createValidationRequest from './requests/ValidationRequest'; import createParcelBuildRequest from './requests/ParcelBuildRequest'; +import createAssetRequest from './requests/AssetRequest'; +import createPathRequest from './requests/PathRequest'; +import {createEnvironment} from './Environment'; +import {createDependency} from './Dependency'; import {Disposable} from '@parcel/events'; import {init as initSourcemaps} from '@parcel/source-map'; import {init as initHash} from '@parcel/hash'; -import {toProjectPath} from './projectPath'; +import { + fromProjectPath, + toProjectPath, + fromProjectPathRelative, +} from './projectPath'; import {tracer} from '@parcel/profiler'; registerCoreWithSerializer(); @@ -437,6 +450,78 @@ export default class Parcel { logger.info({origin: '@parcel/core', message: 'Taking heap snapshot...'}); return this.#farm.takeHeapSnapshot(); } + + async transform(options: ParcelTransformOptions): Promise> { + if (!this.#initialized) { + await this._init(); + } + + let projectRoot = nullthrows(this.#resolvedOptions).projectRoot; + let request = createAssetRequest({ + ...options, + filePath: toProjectPath(projectRoot, options.filePath), + optionsRef: this.#optionsRef, + env: createEnvironment({ + ...options.env, + loc: + options.env?.loc != null + ? { + ...options.env.loc, + filePath: toProjectPath(projectRoot, options.env.loc.filePath), + } + : undefined, + }), + }); + + let res = await this.#requestTracker.runRequest(request, {force: true}); + return res.map(asset => + assetFromValue(asset, nullthrows(this.#resolvedOptions)), + ); + } + + async resolve(request: ParcelResolveOptions): Promise { + if (!this.#initialized) { + await this._init(); + } + + let projectRoot = nullthrows(this.#resolvedOptions).projectRoot; + if (request.resolveFrom == null && path.isAbsolute(request.specifier)) { + request.specifier = fromProjectPathRelative( + toProjectPath(projectRoot, request.specifier), + ); + } + + let dependency = createDependency(projectRoot, { + ...request, + env: createEnvironment({ + ...request.env, + loc: + request.env?.loc != null + ? { + ...request.env.loc, + filePath: toProjectPath(projectRoot, request.env.loc.filePath), + } + : undefined, + }), + }); + + let req = createPathRequest({ + dependency, + name: 'test', + }); + + let res = await this.#requestTracker.runRequest(req, {force: true}); + if (!res) { + return null; + } + + return { + filePath: fromProjectPath(projectRoot, res.filePath), + code: res.code, + query: res.query, + sideEffects: res.sideEffects, + }; + } } export class BuildError extends ThrowableDiagnostic { diff --git a/packages/core/core/test/Parcel.test.js b/packages/core/core/test/Parcel.test.js index 2419cbaef9e..29eefaf8e87 100644 --- a/packages/core/core/test/Parcel.test.js +++ b/packages/core/core/test/Parcel.test.js @@ -75,6 +75,59 @@ describe('Parcel', function () { }); }); +describe('ParcelAPI', function () { + this.timeout(75000); + + let workerFarm; + beforeEach(() => { + workerFarm = createWorkerFarm(); + }); + + afterEach(() => workerFarm.end()); + + describe('parcel.transform()', () => { + it('should transforms simple file', async () => { + let parcel = createParcel({workerFarm}); + let res = await parcel.transform({ + filePath: path.join(__dirname, 'fixtures/parcel/index.js'), + }); + let code = await res[0].getCode(); + assert(code.includes('exports.default = "test"')); + }); + + it('should transform with standalone mode', async () => { + let parcel = createParcel({workerFarm}); + let res = await parcel.transform({ + filePath: path.join(__dirname, 'fixtures/parcel/other.js'), + query: 'standalone=true', + }); + let code = await res[0].getCode(); + + assert(code.includes('require("./index.js")')); + assert(code.includes('new URL("index.js", "file:" + __filename);')); + assert(code.includes('import("index.js")')); + }); + }); + + describe('parcel.resolve()', () => { + it('should resolve dependencies', async () => { + let parcel = createParcel({workerFarm}); + let res = await parcel.resolve({ + specifier: './other', + specifierType: 'esm', + resolveFrom: path.join(__dirname, 'fixtures/parcel/index.js'), + }); + + assert.deepEqual(res, { + filePath: path.join(__dirname, 'fixtures/parcel/other.js'), + code: undefined, + query: undefined, + sideEffects: true, + }); + }); + }); +}); + function createParcel(opts?: InitialParcelOptions) { return new Parcel({ entries: [path.join(__dirname, 'fixtures/parcel/index.js')], diff --git a/packages/core/core/test/fixtures/parcel/index.js b/packages/core/core/test/fixtures/parcel/index.js index e69de29bb2d..2ea084092a2 100644 --- a/packages/core/core/test/fixtures/parcel/index.js +++ b/packages/core/core/test/fixtures/parcel/index.js @@ -0,0 +1 @@ +export default 'test'; diff --git a/packages/core/core/test/fixtures/parcel/other.js b/packages/core/core/test/fixtures/parcel/other.js new file mode 100644 index 00000000000..fc7ecc15c5b --- /dev/null +++ b/packages/core/core/test/fixtures/parcel/other.js @@ -0,0 +1,3 @@ +import * as idx from './index.js'; +new URL('index.js', import.meta.url); +import('index.js') diff --git a/packages/core/types/index.js b/packages/core/types/index.js index 99e80f63c1a..52814f74f07 100644 --- a/packages/core/types/index.js +++ b/packages/core/types/index.js @@ -645,6 +645,28 @@ export type ASTGenerator = {| export type BundleBehavior = 'inline' | 'isolated'; +export type ParcelTransformOptions = {| + filePath: FilePath, + code?: string, + env?: EnvironmentOptions, + pipeline?: ?string, + query?: ?string, +|}; + +export type ParcelResolveOptions = {| + specifier: DependencySpecifier, + specifierType: SpecifierType, + env?: EnvironmentOptions, + resolveFrom?: FilePath, +|}; + +export type ParcelResolveResult = {| + filePath: FilePath, + code?: string, + query?: ?string, + sideEffects?: boolean, +|}; + /** * An asset represents a file or part of a file. It may represent any data type, including source code, * binary data, etc. Assets may exist in the file system or may be virtual. diff --git a/packages/transformers/js/core/src/dependency_collector.rs b/packages/transformers/js/core/src/dependency_collector.rs index 0650ca3fca1..2d268e29b31 100644 --- a/packages/transformers/js/core/src/dependency_collector.rs +++ b/packages/transformers/js/core/src/dependency_collector.rs @@ -125,15 +125,16 @@ impl<'a> DependencyCollector<'a> { None } } - _ => Some(format!( + _ if !self.config.standalone => Some(format!( "{:x}", hash!(format!( "{}:{}:{}", self.get_project_relative_filename(), specifier, kind - )) + )), )), + _ => None, }; self.items.push(DependencyDescriptor { @@ -158,7 +159,7 @@ impl<'a> DependencyCollector<'a> { source_type: SourceType, ) -> ast::Expr { // If not a library, replace with a require call pointing to a runtime that will resolve the url dynamically. - if !self.config.is_library { + if !self.config.is_library && !self.config.standalone { let placeholder = self.add_dependency(specifier.clone(), span, kind, None, false, source_type); let specifier = if let Some(placeholder) = placeholder { @@ -172,13 +173,17 @@ impl<'a> DependencyCollector<'a> { // For library builds, we need to create something that can be statically analyzed by another bundler, // so rather than replacing with a require call that is resolved by a runtime, replace with a `new URL` // call with a placeholder for the relative path to be replaced during packaging. - let placeholder = format!( - "{:x}", - hash!(format!( - "parcel_url:{}:{}:{}", - self.config.filename, specifier, kind - )) - ); + let placeholder = if self.config.standalone { + specifier.as_ref().into() + } else { + format!( + "{:x}", + hash!(format!( + "parcel_url:{}:{}:{}", + self.config.filename, specifier, kind + )) + ) + }; self.items.push(DependencyDescriptor { kind, loc: SourceLocation::from(self.source_map, span), @@ -666,7 +671,7 @@ impl<'a> Fold for DependencyCollector<'a> { // Replace import() with require() if kind == DependencyKind::DynamicImport { let mut call = node; - if !self.config.scope_hoist { + if !self.config.scope_hoist && !self.config.standalone { let name = match &self.config.source_type { SourceType::Module => "require", SourceType::Script => "__parcel__require__", @@ -838,7 +843,7 @@ impl<'a> Fold for DependencyCollector<'a> { // If this is a library, we will already have a URL object. Otherwise, we need to // construct one from the string returned by the JSRuntime. - if !self.config.is_library { + if !self.config.is_library && !self.config.standalone { return Expr::New(NewExpr { span: DUMMY_SP, callee: Box::new(Expr::Ident(Ident::new(js_word!("URL"), DUMMY_SP))), diff --git a/packages/transformers/js/core/src/lib.rs b/packages/transformers/js/core/src/lib.rs index fab82c1cc57..5c582f7ebb9 100644 --- a/packages/transformers/js/core/src/lib.rs +++ b/packages/transformers/js/core/src/lib.rs @@ -84,6 +84,7 @@ pub struct Config { is_esm_output: bool, trace_bailouts: bool, is_swc_helpers: bool, + standalone: bool, } #[derive(Serialize, Debug, Default)] diff --git a/packages/transformers/js/src/JSTransformer.js b/packages/transformers/js/src/JSTransformer.js index 9fb6d75adb2..300edbf94ab 100644 --- a/packages/transformers/js/src/JSTransformer.js +++ b/packages/transformers/js/src/JSTransformer.js @@ -442,6 +442,7 @@ export default (new Transformer({ is_esm_output: asset.env.outputFormat === 'esmodule', trace_bailouts: options.logLevel === 'verbose', is_swc_helpers: /@swc[/\\]helpers/.test(asset.filePath), + standalone: asset.query.has('standalone'), }); let convertLoc = (loc): SourceLocation => { From b8739e265f06df44943ad4c911943fca232c34d1 Mon Sep 17 00:00:00 2001 From: zzheng Date: Wed, 16 Aug 2023 16:42:07 +1000 Subject: [PATCH 2/5] remove pipeline option from .transform, configurable force --- packages/core/core/src/Parcel.js | 18 ++++++++++++++---- packages/core/types/index.js | 3 ++- 2 files changed, 16 insertions(+), 5 deletions(-) diff --git a/packages/core/core/src/Parcel.js b/packages/core/core/src/Parcel.js index 53c9965acfc..ad717c93758 100644 --- a/packages/core/core/src/Parcel.js +++ b/packages/core/core/src/Parcel.js @@ -451,7 +451,10 @@ export default class Parcel { return this.#farm.takeHeapSnapshot(); } - async transform(options: ParcelTransformOptions): Promise> { + async transform({ + shouldDisableCache = false, + ...options + }: ParcelTransformOptions): Promise> { if (!this.#initialized) { await this._init(); } @@ -473,13 +476,18 @@ export default class Parcel { }), }); - let res = await this.#requestTracker.runRequest(request, {force: true}); + let res = await this.#requestTracker.runRequest(request, { + force: shouldDisableCache, + }); return res.map(asset => assetFromValue(asset, nullthrows(this.#resolvedOptions)), ); } - async resolve(request: ParcelResolveOptions): Promise { + async resolve({ + shouldDisableCache = false, + ...request + }: ParcelResolveOptions): Promise { if (!this.#initialized) { await this._init(); } @@ -510,7 +518,9 @@ export default class Parcel { name: 'test', }); - let res = await this.#requestTracker.runRequest(req, {force: true}); + let res = await this.#requestTracker.runRequest(req, { + force: shouldDisableCache, + }); if (!res) { return null; } diff --git a/packages/core/types/index.js b/packages/core/types/index.js index 52814f74f07..13ae2485798 100644 --- a/packages/core/types/index.js +++ b/packages/core/types/index.js @@ -649,8 +649,8 @@ export type ParcelTransformOptions = {| filePath: FilePath, code?: string, env?: EnvironmentOptions, - pipeline?: ?string, query?: ?string, + shouldDisableCache?: boolean, |}; export type ParcelResolveOptions = {| @@ -658,6 +658,7 @@ export type ParcelResolveOptions = {| specifierType: SpecifierType, env?: EnvironmentOptions, resolveFrom?: FilePath, + shouldDisableCache?: boolean, |}; export type ParcelResolveResult = {| From 17f1c27a5908cb98092098afcdcef35ba8479f2c Mon Sep 17 00:00:00 2001 From: zzheng Date: Wed, 16 Aug 2023 16:44:46 +1000 Subject: [PATCH 3/5] Use specifier for name --- packages/core/core/src/Parcel.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/packages/core/core/src/Parcel.js b/packages/core/core/src/Parcel.js index ad717c93758..5e22f975c49 100644 --- a/packages/core/core/src/Parcel.js +++ b/packages/core/core/src/Parcel.js @@ -515,7 +515,7 @@ export default class Parcel { let req = createPathRequest({ dependency, - name: 'test', + name: request.specifier, }); let res = await this.#requestTracker.runRequest(req, { From 6bdbd548f994dd27d626025d532b47f94ed780da Mon Sep 17 00:00:00 2001 From: zzheng Date: Mon, 4 Sep 2023 10:35:57 +1000 Subject: [PATCH 4/5] Update APIs with unstable_ prefix --- packages/core/core/src/Parcel.js | 4 ++-- packages/core/core/test/Parcel.test.js | 8 ++++---- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/core/core/src/Parcel.js b/packages/core/core/src/Parcel.js index 5e22f975c49..ccb25e4a32b 100644 --- a/packages/core/core/src/Parcel.js +++ b/packages/core/core/src/Parcel.js @@ -451,7 +451,7 @@ export default class Parcel { return this.#farm.takeHeapSnapshot(); } - async transform({ + async unstable_transform({ shouldDisableCache = false, ...options }: ParcelTransformOptions): Promise> { @@ -484,7 +484,7 @@ export default class Parcel { ); } - async resolve({ + async unstable_resolve({ shouldDisableCache = false, ...request }: ParcelResolveOptions): Promise { diff --git a/packages/core/core/test/Parcel.test.js b/packages/core/core/test/Parcel.test.js index 29eefaf8e87..36b2681a5e0 100644 --- a/packages/core/core/test/Parcel.test.js +++ b/packages/core/core/test/Parcel.test.js @@ -85,10 +85,10 @@ describe('ParcelAPI', function () { afterEach(() => workerFarm.end()); - describe('parcel.transform()', () => { + describe('parcel.unstable_transform()', () => { it('should transforms simple file', async () => { let parcel = createParcel({workerFarm}); - let res = await parcel.transform({ + let res = await parcel.unstable_transform({ filePath: path.join(__dirname, 'fixtures/parcel/index.js'), }); let code = await res[0].getCode(); @@ -97,7 +97,7 @@ describe('ParcelAPI', function () { it('should transform with standalone mode', async () => { let parcel = createParcel({workerFarm}); - let res = await parcel.transform({ + let res = await parcel.unstable_transform({ filePath: path.join(__dirname, 'fixtures/parcel/other.js'), query: 'standalone=true', }); @@ -112,7 +112,7 @@ describe('ParcelAPI', function () { describe('parcel.resolve()', () => { it('should resolve dependencies', async () => { let parcel = createParcel({workerFarm}); - let res = await parcel.resolve({ + let res = await parcel.unstable_resolve({ specifier: './other', specifierType: 'esm', resolveFrom: path.join(__dirname, 'fixtures/parcel/index.js'), From 2938b372ec1dfbc51849becbc9e48cc83465f6e5 Mon Sep 17 00:00:00 2001 From: zzheng Date: Fri, 8 Sep 2023 13:57:53 +1000 Subject: [PATCH 5/5] remove disableCache option --- packages/core/core/src/Parcel.js | 18 ++++++++---------- packages/core/types/index.js | 2 -- 2 files changed, 8 insertions(+), 12 deletions(-) diff --git a/packages/core/core/src/Parcel.js b/packages/core/core/src/Parcel.js index ccb25e4a32b..028110838eb 100644 --- a/packages/core/core/src/Parcel.js +++ b/packages/core/core/src/Parcel.js @@ -451,10 +451,9 @@ export default class Parcel { return this.#farm.takeHeapSnapshot(); } - async unstable_transform({ - shouldDisableCache = false, - ...options - }: ParcelTransformOptions): Promise> { + async unstable_transform( + options: ParcelTransformOptions, + ): Promise> { if (!this.#initialized) { await this._init(); } @@ -477,17 +476,16 @@ export default class Parcel { }); let res = await this.#requestTracker.runRequest(request, { - force: shouldDisableCache, + force: true, }); return res.map(asset => assetFromValue(asset, nullthrows(this.#resolvedOptions)), ); } - async unstable_resolve({ - shouldDisableCache = false, - ...request - }: ParcelResolveOptions): Promise { + async unstable_resolve( + request: ParcelResolveOptions, + ): Promise { if (!this.#initialized) { await this._init(); } @@ -519,7 +517,7 @@ export default class Parcel { }); let res = await this.#requestTracker.runRequest(req, { - force: shouldDisableCache, + force: true, }); if (!res) { return null; diff --git a/packages/core/types/index.js b/packages/core/types/index.js index e0a98aa2377..31728405c28 100644 --- a/packages/core/types/index.js +++ b/packages/core/types/index.js @@ -653,7 +653,6 @@ export type ParcelTransformOptions = {| code?: string, env?: EnvironmentOptions, query?: ?string, - shouldDisableCache?: boolean, |}; export type ParcelResolveOptions = {| @@ -661,7 +660,6 @@ export type ParcelResolveOptions = {| specifierType: SpecifierType, env?: EnvironmentOptions, resolveFrom?: FilePath, - shouldDisableCache?: boolean, |}; export type ParcelResolveResult = {|