Skip to content

Commit c0ff2fb

Browse files
authored
perf: optimize props destructuring (#2545)
1 parent 7ae5900 commit c0ff2fb

29 files changed

+807
-531
lines changed

.npmrc

+1
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@ engine-strict=true
22
strict-peer-dependencies=false
33
shell-emulator=true
44
auto-install-peers=false
5+
ignore-workspace-root-check=true

package.json

+10-10
Original file line numberDiff line numberDiff line change
@@ -59,31 +59,31 @@
5959
"@napi-rs/triples": "1.1.0",
6060
"@node-rs/helper": "1.3.3",
6161
"@octokit/action": "3.18.1",
62-
"@playwright/test": "1.29.0",
62+
"@playwright/test": "1.29.1",
6363
"@types/brotli": "1.3.1",
6464
"@types/cross-spawn": "6.0.2",
6565
"@types/eslint": "8.4.10",
6666
"@types/express": "4.17.15",
6767
"@types/mri": "1.1.1",
68-
"@types/node": "^18.11.16",
68+
"@types/node": "^18.11.18",
6969
"@types/node-fetch": "2.6.2",
7070
"@types/path-browserify": "1.0.0",
71-
"@types/prettier": "2.7.1",
71+
"@types/prettier": "2.7.2",
7272
"@types/prompts": "2.4.2",
7373
"@types/semver": "7.3.13",
7474
"@types/which-pm-runs": "1.0.0",
75-
"@typescript-eslint/eslint-plugin": "5.46.1",
76-
"@typescript-eslint/parser": "5.46.1",
77-
"@typescript-eslint/utils": "5.46.1",
75+
"@typescript-eslint/eslint-plugin": "5.48.0",
76+
"@typescript-eslint/parser": "5.48.0",
77+
"@typescript-eslint/utils": "5.48.0",
7878
"all-contributors-cli": "6.24.0",
7979
"brotli": "1.3.3",
8080
"commitizen": "4.2.6",
8181
"concurrently": "7.6.0",
8282
"create-qwik": "workspace:*",
8383
"cross-spawn": "7.0.3",
8484
"cz-conventional-changelog": "3.3.0",
85-
"esbuild": "0.16.8",
86-
"eslint": "8.30.0",
85+
"esbuild": "0.16.12",
86+
"eslint": "8.31.0",
8787
"eslint-plugin-no-only-tests": "3.1.0",
8888
"execa": "6.1.0",
8989
"express": "4.18.2",
@@ -96,15 +96,15 @@
9696
"prettier": "2.8.1",
9797
"pretty-quick": "^3.1.3",
9898
"prompts": "2.4.2",
99-
"rollup": "3.7.4",
99+
"rollup": "3.9.1",
100100
"semver": "7.3.8",
101101
"snoop": "^1.0.4",
102102
"terser": "5.16.1",
103103
"tsm": "2.2.2",
104104
"typescript": "4.9.4",
105105
"undici": "5.14.0",
106106
"uvu": "0.5.6",
107-
"vite": "4.0.1",
107+
"vite": "4.0.3",
108108
"vite-tsconfig-paths": "4.0.3",
109109
"watchlist": "0.3.1",
110110
"which-pm-runs": "1.1.0"

packages/docs/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@
4343
"typescript": "4.9.4",
4444
"undici": "5.14.0",
4545
"uvu": "0.5.6",
46-
"vite": "4.0.1",
46+
"vite": "4.0.3",
4747
"wrangler": "^2.6.2"
4848
},
4949
"author": "Builder.io Team",

packages/eslint-plugin-qwik/qwik.unit.ts

+10-10
Original file line numberDiff line numberDiff line change
@@ -302,7 +302,7 @@ export default component$(() => {
302302
return <div></div>
303303
});`,
304304
errors: [
305-
'Identifier ("useMethod") can not be captured inside the scope (component$) because it\'s declared at the root of the module and it is not exported. Add export. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
305+
'Identifier ("useMethod") can not be captured inside the scope (component$) because it\'s declared at the root of the module and it is not exported. Add export. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
306306
],
307307
},
308308
{
@@ -318,7 +318,7 @@ export default component$(() => {
318318
return <div></div>;
319319
});`,
320320
errors: [
321-
'Identifier ("useMethod") can not be captured inside the scope (useTask$) because it is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
321+
'Identifier ("useMethod") can not be captured inside the scope (useTask$) because it is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
322322
],
323323
},
324324
{
@@ -334,7 +334,7 @@ export default component$(() => {
334334
});`,
335335

336336
errors: [
337-
'Identifier ("useMethod") can not be captured inside the scope (useTask$) because it is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
337+
'Identifier ("useMethod") can not be captured inside the scope (useTask$) because it is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
338338
],
339339
},
340340
{
@@ -348,7 +348,7 @@ export default component$(() => {
348348
});`,
349349

350350
errors: [
351-
'Identifier ("Stuff") can not be captured inside the scope (useTask$) because it is a class constructor, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
351+
'Identifier ("Stuff") can not be captured inside the scope (useTask$) because it is a class constructor, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
352352
],
353353
},
354354
{
@@ -363,7 +363,7 @@ export default component$(() => {
363363
});`,
364364

365365
errors: [
366-
'Identifier ("stuff") can not be captured inside the scope (useTask$) because it is an instance of the "Stuff" class, which is not serializable. Use a simple object literal instead. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
366+
'Identifier ("stuff") can not be captured inside the scope (useTask$) because it is an instance of the "Stuff" class, which is not serializable. Use a simple object literal instead. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
367367
],
368368
},
369369
{
@@ -378,7 +378,7 @@ export default component$(() => {
378378
});`,
379379

380380
errors: [
381-
'Identifier ("a") can not be captured inside the scope (useTask$) because it is Symbol, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
381+
'Identifier ("a") can not be captured inside the scope (useTask$) because it is Symbol, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
382382
],
383383
},
384384
{
@@ -400,7 +400,7 @@ export default component$(() => {
400400
});`,
401401

402402
errors: [
403-
'Identifier ("a") can not be captured inside the scope (useTask$) because it is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
403+
'Identifier ("a") can not be captured inside the scope (useTask$) because it is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
404404
],
405405
},
406406
{
@@ -417,7 +417,7 @@ export default component$(() => {
417417
return <div></div>
418418
});`,
419419
errors: [
420-
'Identifier ("state") can not be captured inside the scope (useTask$) because "state.value" is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
420+
'Identifier ("state") can not be captured inside the scope (useTask$) because "state.value" is a function, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
421421
],
422422
},
423423
{
@@ -448,7 +448,7 @@ export default component$(() => {
448448
);
449449
});`,
450450
errors: [
451-
'The value of the identifier ("click") can not be changed once it is captured the scope (onClick$). Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
451+
'The value of the identifier ("click") can not be changed once it is captured the scope (onClick$). Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
452452
],
453453
},
454454
{
@@ -465,7 +465,7 @@ export default component$(() => {
465465
);
466466
});`,
467467
errors: [
468-
'Identifier ("props") can not be captured inside the scope (onClick$) because "props.nonserializableTuple" is an instance of the "Function" class, which is not serializable. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
468+
'Identifier ("props") can not be captured inside the scope (onClick$) because "props.nonserializableTuple" is an instance of the "Function" class, which is not serializable. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
469469
],
470470
},
471471
],

packages/eslint-plugin-qwik/src/validLexicalScope.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -40,11 +40,11 @@ export const validLexicalScope = createRule({
4040

4141
messages: {
4242
referencesOutside:
43-
'Identifier ("{{varName}}") can not be captured inside the scope ({{dollarName}}) because {{reason}}. Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
43+
'Identifier ("{{varName}}") can not be captured inside the scope ({{dollarName}}) because {{reason}}. Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
4444
unvalidJsxDollar:
4545
'JSX attributes that end with $ can only take an inlined arrow function of a QRL identifier. Make sure the value is created using $()',
4646
mutableIdentifier:
47-
'The value of the identifier ("{{varName}}") can not be changed once it is captured the scope ({{dollarName}}). Check out https://qwik.builder.io/docs/advanced/optimizer for more details.',
47+
'The value of the identifier ("{{varName}}") can not be changed once it is captured the scope ({{dollarName}}). Check out https://qwik.builder.io/docs/advanced/dollar/ for more details.',
4848
},
4949
},
5050
create(context) {

packages/qwik-city/package.json

+4-4
Original file line numberDiff line numberDiff line change
@@ -82,13 +82,13 @@
8282
"@netlify/edge-functions": "^2.0.0",
8383
"@types/marked": "4.0.8",
8484
"@types/mdast": "^3.0.10",
85-
"@types/node": "^18.11.16",
85+
"@types/node": "^18.11.18",
8686
"@types/refractor": "3.0.2",
8787
"estree-util-value-to-estree": "2.1.0",
8888
"github-slugger": "2.0.0",
8989
"hast-util-heading-rank": "2.1.0",
9090
"hast-util-to-string": "2.0.0",
91-
"marked": "4.2.4",
91+
"marked": "4.2.5",
9292
"mdast-util-mdx": "^2.0.0",
9393
"refractor": "4.8.0",
9494
"rehype-autolink-headings": "6.1.1",
@@ -99,8 +99,8 @@
9999
"unified": "10.1.2",
100100
"unist-util-visit": "4.1.1",
101101
"uvu": "0.5.6",
102-
"vite": "4.0.1",
103-
"yaml": "2.1.3"
102+
"vite": "4.0.3",
103+
"yaml": "2.2.1"
104104
},
105105
"peerDependencies": {
106106
"@builder.io/qwik": ">=0.16.0"

packages/qwik-react/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,11 @@
2020
"devDependencies": {
2121
"@builder.io/qwik": "workspace:*",
2222
"@types/react": "18.0.26",
23-
"@types/react-dom": "18.0.9",
23+
"@types/react-dom": "18.0.10",
2424
"react": "18.2.0",
2525
"react-dom": "18.2.0",
2626
"typescript": "4.9.4",
27-
"vite": "4.0.1"
27+
"vite": "4.0.3"
2828
},
2929
"peerDependencies": {
3030
"@builder.io/qwik": ">=0.1.11",

packages/qwik/src/core/api.md

+3
Original file line numberDiff line numberDiff line change
@@ -740,6 +740,9 @@ export interface ResourceResolved<T> {
740740
// @public (undocumented)
741741
export type ResourceReturn<T> = ResourcePending<T> | ResourceResolved<T> | ResourceRejected<T>;
742742

743+
// @internal (undocumented)
744+
export const _restProps: (props: Record<string, any>, omit: string[]) => Record<string, any>;
745+
743746
// @alpha
744747
export const setPlatform: (plt: CorePlatform) => CorePlatform;
745748

packages/qwik/src/core/index.ts

+1
Original file line numberDiff line numberDiff line change
@@ -116,6 +116,7 @@ export type { ValueOrPromise } from './util/types';
116116
export type { Signal } from './state/signal';
117117
export type { NoSerialize } from './state/common';
118118
export { _wrapSignal } from './state/signal';
119+
export { _restProps } from './state/store';
119120
export { noSerialize, mutable } from './state/common';
120121
export { _IMMUTABLE } from './state/constants';
121122

packages/qwik/src/core/state/store.ts

+13-8
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,19 @@ export const setObjectFlags = (obj: object, flags: number) => {
7878

7979
export type TargetType = Record<string | symbol, any>;
8080

81+
/**
82+
* @internal
83+
*/
84+
export const _restProps = (props: Record<string, any>, omit: string[]) => {
85+
const rest: Record<string, any> = {};
86+
for (const key in props) {
87+
if (!omit.includes(key)) {
88+
rest[key] = props[key];
89+
}
90+
}
91+
return rest;
92+
};
93+
8194
class ReadWriteProxyHandler implements ProxyHandler<TargetType> {
8295
constructor(
8396
private $containerState$: ContainerState,
@@ -174,14 +187,6 @@ class ReadWriteProxyHandler implements ProxyHandler<TargetType> {
174187
}
175188

176189
ownKeys(target: TargetType): ArrayLike<string | symbol> {
177-
let subscriber: SubscriberHost | SubscriberEffect | null | undefined = null;
178-
const invokeCtx = tryGetInvokeContext();
179-
if (invokeCtx) {
180-
subscriber = invokeCtx.$subscriber$;
181-
}
182-
if (subscriber) {
183-
this.$manager$.$addSub$([0, subscriber, undefined]);
184-
}
185190
if (isArray(target)) {
186191
return Reflect.ownKeys(target);
187192
}

packages/qwik/src/optimizer/core/src/code_move.rs

+1-1
Original file line numberDiff line numberDiff line change
@@ -384,7 +384,7 @@ fn transform_fn(node: ast::FnExpr, use_lexical_scope: &Id, scoped_idents: &[Id])
384384
}
385385
}
386386

387-
const fn create_return_stmt(expr: Box<ast::Expr>) -> ast::Stmt {
387+
pub const fn create_return_stmt(expr: Box<ast::Expr>) -> ast::Stmt {
388388
ast::Stmt::Return(ast::ReturnStmt {
389389
arg: Some(expr),
390390
span: DUMMY_SP,

packages/qwik/src/optimizer/core/src/collector.rs

+13-3
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,7 @@ pub struct Import {
4242
}
4343

4444
pub struct GlobalCollect {
45+
pub synthetic: Vec<(Id, Import)>,
4546
pub imports: HashMap<Id, Import>,
4647
pub exports: HashMap<Id, Option<JsWord>>,
4748
pub root: HashMap<Id, Span>,
@@ -52,6 +53,7 @@ pub struct GlobalCollect {
5253

5354
pub fn global_collect(module: &ast::Module) -> GlobalCollect {
5455
let mut collect = GlobalCollect {
56+
synthetic: vec![],
5557
imports: HashMap::with_capacity(16),
5658
exports: HashMap::with_capacity(16),
5759

@@ -96,6 +98,9 @@ impl GlobalCollect {
9698
}
9799

98100
pub fn add_import(&mut self, local: Id, import: Import) {
101+
if import.synthetic {
102+
self.synthetic.push((local.clone(), import.clone()));
103+
}
99104
self.rev_imports.insert(
100105
(import.specifier.clone(), import.source.clone()),
101106
local.clone(),
@@ -370,25 +375,29 @@ impl Visit for IdentCollector {
370375
}
371376
}
372377

373-
pub fn collect_from_pat(pat: &ast::Pat, identifiers: &mut Vec<(Id, Span)>) {
378+
pub fn collect_from_pat(pat: &ast::Pat, identifiers: &mut Vec<(Id, Span)>) -> bool {
374379
match pat {
375380
ast::Pat::Ident(ident) => {
376381
identifiers.push((id!(ident.id), ident.id.span));
382+
true
377383
}
378384
ast::Pat::Array(array) => {
379385
for el in array.elems.iter().flatten() {
380386
collect_from_pat(el, identifiers);
381387
}
388+
false
382389
}
383390
ast::Pat::Rest(rest) => {
384391
if let ast::Pat::Ident(ident) = rest.arg.as_ref() {
385392
identifiers.push((id!(ident.id), ident.id.span));
386393
}
394+
false
387395
}
388396
ast::Pat::Assign(expr) => {
389397
if let ast::Pat::Ident(ident) = expr.left.as_ref() {
390398
identifiers.push((id!(ident.id), ident.id.span));
391399
}
400+
false
392401
}
393402
ast::Pat::Object(obj) => {
394403
for prop in &obj.props {
@@ -406,7 +415,8 @@ pub fn collect_from_pat(pat: &ast::Pat, identifiers: &mut Vec<(Id, Span)>) {
406415
}
407416
}
408417
}
418+
false
409419
}
410-
_ => {}
411-
};
420+
_ => false,
421+
}
412422
}

packages/qwik/src/optimizer/core/src/lib.rs

+1
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@ mod filter_exports;
1717
mod is_immutable;
1818
mod package_json;
1919
mod parse;
20+
mod props_destructuring;
2021
mod transform;
2122
mod utils;
2223
mod words;

packages/qwik/src/optimizer/core/src/parse.rs

+5-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ use crate::collector::global_collect;
1010
use crate::const_replace::ConstReplacerVisitor;
1111
use crate::entry_strategy::EntryPolicy;
1212
use crate::filter_exports::StripExportsVisitor;
13+
use crate::props_destructuring::transform_props_destructuring;
1314
use crate::transform::{HookKind, QwikTransform, QwikTransformOptions};
1415
use crate::utils::{Diagnostic, DiagnosticCategory, DiagnosticScope, SourceLocation};
1516
use path_slash::PathExt;
@@ -283,8 +284,11 @@ pub fn transform_code(config: TransformCodeOptions) -> Result<TransformOutput, a
283284
));
284285

285286
// Collect import/export metadata
286-
let collect = global_collect(&main_module);
287+
let mut collect = global_collect(&main_module);
287288

289+
transform_props_destructuring(&mut main_module, &mut collect);
290+
291+
// Replace const values
288292
if let Some(is_server) = config.is_server {
289293
let mut const_replacer = ConstReplacerVisitor::new(is_server, &collect);
290294
main_module.visit_mut_with(&mut const_replacer);

0 commit comments

Comments
 (0)