Skip to content

Commit

Permalink
Update examples for memoisation and move back to packager
Browse files Browse the repository at this point in the history
  • Loading branch information
JakeLane committed Aug 5, 2024
1 parent 99574ef commit 9b0230b
Show file tree
Hide file tree
Showing 9 changed files with 121 additions and 113 deletions.
2 changes: 1 addition & 1 deletion packages/examples/phases/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
"private": true,
"scripts": {
"start": "parcel serve src/index.html --no-cache --https --feature-flag tieredImports=true",
"start:prod": "yarn build && npx http-server dist/",
"start:prod": "yarn build && npx http-server -p 1234 dist/",
"build": "PARCEL_WORKERS=0 parcel build src/index.html --no-cache --feature-flag tieredImports=true",
"debug": "PARCEL_WORKERS=0 node --inspect-brk $(yarn bin parcel) serve src/index.html --no-cache --https --feature-flag tieredImports=true --no-hmr",
"debug:prod": "PARCEL_WORKERS=0 node --inspect-brk $(yarn bin parcel) build src/index.html --no-cache --feature-flag tieredImports=true"
Expand Down
36 changes: 24 additions & 12 deletions packages/examples/phases/src/utils.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,21 @@
import React, {
ComponentType,
ForwardRefExoticComponent,
ForwardedRef,
MemoExoticComponent,
PropsWithChildren,
PropsWithoutRef,
RefAttributes,
forwardRef,
memo,
} from 'react';

export function deferredLoadComponent<P extends {[k: string]: any} | undefined>(
resource: DeferredImport<{default: ComponentType<P>}>,
): ForwardRefExoticComponent<
PropsWithoutRef<P> & RefAttributes<ComponentType<P>>
): MemoExoticComponent<
ForwardRefExoticComponent<
PropsWithoutRef<P> & RefAttributes<ComponentType<P>>
>
> {
// Create a deferred component map in the global context, so we can reuse the components everywhere
if (!globalThis.deferredComponentMap) {
Expand All @@ -21,25 +27,31 @@ export function deferredLoadComponent<P extends {[k: string]: any} | undefined>(
}

let Component: ComponentType | undefined;
const loader = new Promise(resolve => {
let loader = new Promise(resolve => {
resource.onReady(loaded => {
Component = loaded;
resolve(loaded);
});
});

const wrapper = forwardRef<ComponentType<P>, P>(function DeferredComponent(
props,
ref,
const wrapper = function DeferredComponent(
props: PropsWithChildren<P>,
ref: ForwardedRef<ComponentType<P>>,
) {
if (Component) {
return <Component {...props} ref={ref} />;
} else {
throw loader;
}
});

// Store in weakmap so we only have one instance
globalThis.deferredComponentMap.set(resource, wrapper);
return wrapper;
throw loader;
};

// Support refs in the deferred component
const forwardedRef = forwardRef(wrapper);

// Memoise so we avoid re-renders
const memoised = memo(forwardedRef);

// Store in weak map so we only have one instance
globalThis.deferredComponentMap.set(resource, memoised);
return memoised;
}
23 changes: 16 additions & 7 deletions packages/packagers/js/src/ScopeHoistingPackager.js
Original file line number Diff line number Diff line change
Expand Up @@ -741,13 +741,22 @@ ${code}
}

let symbol = this.getSymbolResolution(asset, resolved, imported, dep);
replacements.set(
local,
// If this was an internalized async asset, wrap in a Promise.resolve.
asyncResolution?.type === 'asset'
? `Promise.resolve(${symbol})`
: symbol,
);
if (
this.options.featureFlags.tieredImports &&
dep.priority === 'tier'
) {
// Wrap tiered import symbols with tier helper
replacements.set(local, `$parcel$tier(${symbol})`);
this.usedHelpers.add('$parcel$tier');
} else {
replacements.set(
local,
// If this was an internalized async asset, wrap in a Promise.resolve.
asyncResolution?.type === 'asset'
? `Promise.resolve(${symbol})`
: symbol,
);
}
}

// Async dependencies need a namespace object even if all used symbols were statically analyzed.
Expand Down
23 changes: 23 additions & 0 deletions packages/packagers/js/src/dev-prelude.js
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,29 @@
{},
];
};
newRequire.tier = function (loader) {
var listeners = new Set();
var resolved = false;
if (loader instanceof Promise) {
loader.then(mod => {
resolved = true;
for (let listener of listeners) {
listener?.(mod.default);
}
});
} else {
resolved = true;
}
return {
onReady: listener => {
if (resolved) {
listener(loader.default);
} else {
listeners.add(listener);
}
},
};
};

Object.defineProperty(newRequire, 'root', {
get: function () {
Expand Down
27 changes: 27 additions & 0 deletions packages/packagers/js/src/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -160,10 +160,37 @@ function $parcel$defineInteropFlag(a) {
}
`;

const $parcel$tier = `
function $parcel$tier(loader) {
var listeners = new Set();
var resolved = false;
if (loader instanceof Promise) {
loader.then(mod => {
resolved = true;
for (let listener of listeners) {
listener?.(mod.default);
}
});
} else {
resolved = true;
}
return {
onReady: listener => {
if (resolved) {
listener(loader.default);
} else {
listeners.add(listener);
}
},
};
}
`;

export const helpers = {
$parcel$export,
$parcel$exportWildcard,
$parcel$interopDefault,
$parcel$global,
$parcel$defineInteropFlag,
$parcel$tier,
};
66 changes: 20 additions & 46 deletions packages/transformers/js/core/src/dependency_collector.rs
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ use std::hash::Hash;
use std::hash::Hasher;
use std::path::Path;
use swc_core::ecma::ast::MemberExpr;
use swc_core::ecma::ast::Module;
use swc_core::ecma::utils::stack_size::maybe_grow_default;

use path_slash::PathBufExt;
Expand Down Expand Up @@ -134,15 +133,14 @@ pub struct DependencyDescriptor {

/// This pass collects dependencies in a module and compiles references as needed to work with Parcel's JSRuntime.
pub fn dependency_collector<'a>(
module: Module,
source_map: &'a SourceMap,
items: &'a mut Vec<DependencyDescriptor>,
ignore_mark: swc_core::common::Mark,
unresolved_mark: swc_core::common::Mark,
config: &'a Config,
diagnostics: &'a mut Vec<Diagnostic>,
) -> (Module, bool) {
let mut fold = DependencyCollector {
) -> impl Fold + 'a {
DependencyCollector {
source_map,
items,
in_try: false,
Expand All @@ -153,12 +151,7 @@ pub fn dependency_collector<'a>(
config,
diagnostics,
import_meta: None,
needs_tier_helpers: false,
};

let module = module.fold_with(&mut fold);

(module, fold.needs_tier_helpers)
}
}

struct DependencyCollector<'a> {
Expand All @@ -172,7 +165,6 @@ struct DependencyCollector<'a> {
config: &'a Config,
diagnostics: &'a mut Vec<Diagnostic>,
import_meta: Option<ast::VarDecl>,
needs_tier_helpers: bool,
}

impl<'a> DependencyCollector<'a> {
Expand Down Expand Up @@ -340,26 +332,6 @@ impl<'a> Fold for DependencyCollector<'a> {
);
}

if self.needs_tier_helpers {
res.body.insert(
0,
ast::ModuleItem::Stmt(ast::Stmt::Decl(ast::Decl::Var(Box::new(ast::VarDecl {
span: DUMMY_SP,
kind: ast::VarDeclKind::Var,
decls: vec![ast::VarDeclarator {
span: DUMMY_SP,
name: ast::Pat::Ident(ast::Ident::new("parcel$tier$".into(), DUMMY_SP).into()),
init: Some(Box::new(ast::Expr::Call(crate::utils::create_require(
"@parcel/transformer-js/src/tier-helpers.js".into(),
self.unresolved_mark,
)))),
definite: false,
}],
declare: false,
})))),
);
}

res
}

Expand Down Expand Up @@ -830,9 +802,6 @@ impl<'a> Fold for DependencyCollector<'a> {
});
}

// Track that we need to add the dependency to the asset
self.needs_tier_helpers = true;

// Convert to require without scope hoisting
if !self.config.scope_hoist && !self.config.standalone {
let name = match &self.config.source_type {
Expand All @@ -849,19 +818,24 @@ impl<'a> Fold for DependencyCollector<'a> {
let rewritten_call = rewrite_require_specifier(call, self.unresolved_mark);
self.require_node = Some(rewritten_call.clone());

// Wrap with the parcelTiers helper
ast::CallExpr {
span: DUMMY_SP,
type_args: None,
args: vec![rewritten_call.as_arg()],
callee: ast::Callee::Expr(Box::new(Member(MemberExpr {
obj: Box::new(ast::Expr::Ident(ast::Ident::new(
"parcel$tier$".into(),
DUMMY_SP,
))),
prop: MemberProp::Ident(ast::Ident::new("load".into(), DUMMY_SP)),
if !self.config.scope_hoist && !self.config.standalone {
// Wrap with parcel tiers when not scope hoisting
ast::CallExpr {
span: DUMMY_SP,
}))),
type_args: None,
args: vec![rewritten_call.as_arg()],
callee: ast::Callee::Expr(Box::new(Member(MemberExpr {
obj: Box::new(Member(MemberExpr {
obj: Box::new(ast::Expr::Ident(ast::Ident::new("module".into(), DUMMY_SP))),
prop: MemberProp::Ident(ast::Ident::new("bundle".into(), DUMMY_SP)),
span: DUMMY_SP,
})),
prop: MemberProp::Ident(ast::Ident::new("tier".into(), DUMMY_SP)),
span: DUMMY_SP,
}))),
}
} else {
rewritten_call
}
}
_ => node.fold_children_with(self),
Expand Down
20 changes: 10 additions & 10 deletions packages/transformers/js/core/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -145,7 +145,6 @@ pub struct TransformResult {
pub used_env: HashSet<swc_core::ecma::atoms::JsWord>,
pub has_node_replacements: bool,
pub is_constant_module: bool,
pub needs_tier_helpers: bool,
}

fn targets_to_versions(targets: &Option<HashMap<String, String>>) -> Option<Versions> {
Expand Down Expand Up @@ -460,16 +459,17 @@ pub fn transform(
};

let ignore_mark = Mark::fresh(Mark::root());
let (module, needs_tier_helpers) = dependency_collector(
module,
&source_map,
&mut result.dependencies,
ignore_mark,
unresolved_mark,
&config,
&mut diagnostics,
let module = module.fold_with(
// Collect dependencies
&mut dependency_collector(
&source_map,
&mut result.dependencies,
ignore_mark,
unresolved_mark,
&config,
&mut diagnostics,
),
);
result.needs_tier_helpers = needs_tier_helpers;

diagnostics.extend(error_buffer_to_diagnostics(&error_buffer, &source_map));

Expand Down
14 changes: 0 additions & 14 deletions packages/transformers/js/src/JSTransformer.js
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,6 @@ export default (new Transformer({
hoist_result,
symbol_result,
needs_esm_helpers,
needs_tier_helpers,
diagnostics,
used_env,
has_node_replacements,
Expand Down Expand Up @@ -1071,19 +1070,6 @@ export default (new Transformer({
}
}

if (needs_tier_helpers) {
asset.addDependency({
specifier: '@parcel/transformer-js/src/tier-helpers.js',
specifierType: 'esm',
resolveFrom: __filename,
env: {
includeNodeModules: {
'@parcel/transformer-js': true,
},
},
});
}

asset.type = 'js';
asset.setBuffer(compiledCode);

Expand Down
23 changes: 0 additions & 23 deletions packages/transformers/js/src/tier-helpers.js

This file was deleted.

0 comments on commit 9b0230b

Please sign in to comment.