Skip to content

Commit

Permalink
refactor(qwik-loader): optimize + remove cruft
Browse files Browse the repository at this point in the history
- qEvents is no longer used
- qwikloader.optimize.js is the same as regular, remove
- some tricks to allow smaller code after minify
- remove @vite-ignore comment
  • Loading branch information
wmertens committed Feb 22, 2024
1 parent abcb576 commit b05b7ae
Show file tree
Hide file tree
Showing 13 changed files with 55 additions and 129 deletions.
2 changes: 1 addition & 1 deletion packages/docs/src/routes/api/qwik-server/api.json
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,7 @@
}
],
"kind": "Interface",
"content": "```typescript\nexport interface QwikLoaderOptions \n```\n\n\n| Property | Modifiers | Type | Description |\n| --- | --- | --- | --- |\n| [events?](#) | | string\\[\\] | _(Optional)_ |\n| [include?](#) | | 'always' \\| 'never' \\| 'auto' | _(Optional)_ |\n| [position?](#) | | 'top' \\| 'bottom' | _(Optional)_ |",
"content": "```typescript\nexport interface QwikLoaderOptions \n```\n\n\n| Property | Modifiers | Type | Description |\n| --- | --- | --- | --- |\n| [include?](#) | | 'always' \\| 'never' \\| 'auto' | _(Optional)_ |\n| [position?](#) | | 'top' \\| 'bottom' | _(Optional)_ |",
"editUrl": "https://github.com/BuilderIO/qwik/tree/main/packages/qwik/src/server/types.ts",
"mdFile": "qwik.qwikloaderoptions.md"
},
Expand Down
1 change: 0 additions & 1 deletion packages/docs/src/routes/api/qwik-server/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -138,7 +138,6 @@ export interface QwikLoaderOptions

| Property | Modifiers | Type | Description |
| -------------- | --------- | ----------------------------- | ------------ |
| [events?](#) | | string[] | _(Optional)_ |
| [include?](#) | | 'always' \| 'never' \| 'auto' | _(Optional)_ |
| [position?](#) | | 'top' \| 'bottom' | _(Optional)_ |

Expand Down
2 changes: 0 additions & 2 deletions packages/qwik/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -161,8 +161,6 @@
"qwik-prefetch.js",
"qwikloader.js",
"qwikloader.debug.js",
"qwikloader.optimize.js",
"qwikloader.optimize.debug.js",
"qwik-cli.cjs",
"server.cjs",
"server.mjs",
Expand Down
4 changes: 1 addition & 3 deletions packages/qwik/src/core/container/pause.ts
Original file line number Diff line number Diff line change
Expand Up @@ -196,9 +196,7 @@ export const pauseContainer = async (
// Emit event registration
const extraListeners = Array.from(containerState.$events$, (s) => JSON.stringify(s));
const eventsScript = doc.createElement('script');
eventsScript.textContent = `window.qwikevents||=[];window.qwikevents.push(${extraListeners.join(
', '
)})`;
eventsScript.textContent = `(window.qwikevents||=[]).push(${extraListeners.join(', ')})`;
parentJSON.appendChild(eventsScript);

return data;
Expand Down
3 changes: 1 addition & 2 deletions packages/qwik/src/core/container/store.unit.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -49,8 +49,7 @@ test.skip('should serialize content', async () => {
<!--/qv-->
</div>
<script>
window.qwikevents ||= [];
window.qwikevents.push("click");
(window.qwikevents ||= []).push("click");
</script>
</body>`
);
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/core/render/dom/visitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1235,8 +1235,8 @@ export const registerQwikEvent = (prop: string) => {
if (!qTest) {
const eventName = getEventName(prop);
try {
const qwikevents = ((globalThis as any).qwikevents ||= []);
qwikevents.push(eventName);
// This is managed by qwik-loader
((globalThis as any).qwikevents ||= []).push(eventName);
} catch (err) {
logWarn(err);
}
Expand Down
56 changes: 30 additions & 26 deletions packages/qwik/src/qwikloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,28 +16,32 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
const win = window as any;
const events = new Set();

// Some shortenings for minification
const replace = 'replace';
const forEach = 'forEach';
const target = 'target';
const getAttribute = 'getAttribute';
const isConnected = 'isConnected';
const qvisible = 'qvisible';
const Q_JSON = '_qwikjson_';
const querySelectorAll = (query: string) => {
return doc.querySelectorAll(query);
};

const broadcast = (infix: string, ev: Event, type = ev.type) => {
querySelectorAll('[on' + infix + '\\:' + type + ']').forEach((target) =>
dispatch(target, infix, ev, type)
querySelectorAll('[on' + infix + '\\:' + type + ']')[forEach]((el) =>
dispatch(el, infix, ev, type)
);
};

const getAttribute = (el: Element, name: string) => {
return el.getAttribute(name);
};

const resolveContainer = (containerEl: Element) => {
if ((containerEl as QContainerElement)['_qwikjson_'] === undefined) {
if ((containerEl as QContainerElement)[Q_JSON] === undefined) {
const parentJSON = containerEl === doc.documentElement ? doc.body : containerEl;
let script = parentJSON.lastElementChild;
while (script) {
if (script.tagName === 'SCRIPT' && getAttribute(script, 'type') === 'qwik/json') {
(containerEl as QContainerElement)['_qwikjson_'] = JSON.parse(
script.textContent!.replace(/\\x3C(\/?script)/gi, '<$1')
if (script.tagName === 'SCRIPT' && script[getAttribute]('type') === 'qwik/json') {
(containerEl as QContainerElement)[Q_JSON] = JSON.parse(
script.textContent![replace](/\\x3C(\/?script)/gi, '<$1')
);
break;
}
Expand All @@ -57,21 +61,21 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
ev.preventDefault();
}
const ctx = (element as any)['_qc_'] as QContext | undefined;
const relevantListeners = ctx?.li.filter((li) => li[0] === attrName);
const relevantListeners = ctx && ctx.li.filter((li) => li[0] === attrName);
if (relevantListeners && relevantListeners.length > 0) {
for (const listener of relevantListeners) {
// listener[1] holds the QRL
await listener[1].getFn([element, ev], () => element.isConnected)(ev, element);
await listener[1].getFn([element, ev], () => element[isConnected])(ev, element);
}
return;
}
const attrValue = getAttribute(element, attrName);
const attrValue = element[getAttribute](attrName);
if (attrValue) {
const container = element.closest('[q\\:container]')!;
const base = new URL(getAttribute(container, 'q:base')!, doc.baseURI);
const base = new URL(container[getAttribute]('q:base')!, doc.baseURI);
for (const qrl of attrValue.split('\n')) {
const url = new URL(qrl, base);
const symbolName = url.hash.replace(/^#?([^?[|]*).*$/, '$1') || 'default';
const symbolName = url.hash[replace](/^#?([^?[|]*).*$/, '$1') || 'default';
const reqTime = performance.now();
let handler: any;
const isSync = qrl.startsWith('#');
Expand All @@ -83,7 +87,7 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
handler = (await module)[symbolName];
}
const previousCtx = (doc as any)[Q_CONTEXT];
if (element.isConnected) {
if (element[isConnected]) {
try {
(doc as any)[Q_CONTEXT] = [element, ev, url];
isSync ||
Expand All @@ -105,7 +109,7 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
doc.dispatchEvent(createEvent<T>(eventName, detail));
};

const camelToKebab = (str: string) => str.replace(/([A-Z])/g, (a) => '-' + a.toLowerCase());
const camelToKebab = (str: string) => str[replace](/([A-Z])/g, (a) => '-' + a.toLowerCase());

/**
* Event handler responsible for processing browser events.
Expand All @@ -118,10 +122,10 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
const processDocumentEvent = async (ev: Event) => {
// eslint-disable-next-line prefer-const
let type = camelToKebab(ev.type);
let element = ev.target as Element | null;
let element = ev[target] as Element | null;
broadcast('-document', ev, type);

while (element && element.getAttribute) {
while (element && element[getAttribute]) {
await dispatch(element, '', ev, type);
element = ev.bubbles && ev.cancelBubble !== true ? element.parentElement : null;
}
Expand All @@ -141,17 +145,17 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
const riC = win.requestIdleCallback ?? win.setTimeout;
riC.bind(win)(() => emitEvent('qidle'));

if (events.has('qvisible')) {
const results = querySelectorAll('[on\\:qvisible]');
if (events.has(qvisible)) {
const results = querySelectorAll('[on\\:' + qvisible + ']');
const observer = new IntersectionObserver((entries) => {
for (const entry of entries) {
if (entry.isIntersecting) {
observer.unobserve(entry.target);
dispatch(entry.target, '', createEvent<QwikVisibleEvent>('qvisible', entry));
observer.unobserve(entry[target]);
dispatch(entry[target], '', createEvent<QwikVisibleEvent>(qvisible, entry));
}
}
});
results.forEach((el) => observer.observe(el));
results[forEach]((el) => observer.observe(el));
}
}
};
Expand All @@ -176,8 +180,8 @@ export const qwikLoader = (doc: Document, hasInitialized?: number) => {
};

if (!(Q_CONTEXT in doc)) {
// Mark qwik-loader presence
(doc as any)[Q_CONTEXT] = undefined;
// Mark qwik-loader presence but falsy
(doc as any)[Q_CONTEXT] = 0;
const qwikevents = win.qwikevents;
// If `qwikEvents` is an array, process it.
if (Array.isArray(qwikevents)) {
Expand Down
2 changes: 0 additions & 2 deletions packages/qwik/src/server/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,8 +68,6 @@ export interface PrefetchStrategy {

// @public (undocumented)
export interface QwikLoaderOptions {
// (undocumented)
events?: string[];
// (undocumented)
include?: 'always' | 'never' | 'auto';
// (undocumented)
Expand Down
9 changes: 4 additions & 5 deletions packages/qwik/src/server/render.ts
Original file line number Diff line number Diff line change
Expand Up @@ -195,7 +195,6 @@ export async function renderToStream(
const includeLoader = includeMode === 'always' || (includeMode === 'auto' && needLoader);
if (includeLoader) {
const qwikLoaderScript = getQwikLoaderScript({
events: opts.qwikLoader?.events,
debug: opts.debug,
});
children.push(
Expand All @@ -207,12 +206,12 @@ export async function renderToStream(
);
}

// We emit the events separately so other qwikloaders can see them
const extraListeners = Array.from(containerState.$events$, (s) => JSON.stringify(s));
if (extraListeners.length > 0) {
let content = `window.qwikevents.push(${extraListeners.join(', ')})`;
if (!includeLoader) {
content = `window.qwikevents||=[];${content}`;
}
const content =
(includeLoader ? `window.qwikevents` : `(window.qwikevents||=[])`) +
`.push(${extraListeners.join(', ')})`;
children.push(
jsx('script', {
dangerouslySetInnerHTML: content,
Expand Down
8 changes: 0 additions & 8 deletions packages/qwik/src/server/scripts.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,5 @@
const QWIK_LOADER_DEFAULT_MINIFIED: string = (globalThis as any).QWIK_LOADER_DEFAULT_MINIFIED;
const QWIK_LOADER_DEFAULT_DEBUG: string = (globalThis as any).QWIK_LOADER_DEFAULT_DEBUG;
const QWIK_LOADER_OPTIMIZE_MINIFIED: string = (globalThis as any).QWIK_LOADER_OPTIMIZE_MINIFIED;
const QWIK_LOADER_OPTIMIZE_DEBUG: string = (globalThis as any).QWIK_LOADER_OPTIMIZE_DEBUG;

/**
* Provides the `qwikloader.js` file as a string. Useful for tooling to inline the qwikloader script
Expand All @@ -10,12 +8,6 @@ const QWIK_LOADER_OPTIMIZE_DEBUG: string = (globalThis as any).QWIK_LOADER_OPTIM
* @public
*/
export function getQwikLoaderScript(opts: { events?: string[]; debug?: boolean } = {}) {
if (Array.isArray(opts.events) && opts.events.length > 0) {
// inject exact known events used
const loader = opts.debug ? QWIK_LOADER_OPTIMIZE_DEBUG : QWIK_LOADER_OPTIMIZE_MINIFIED;
return loader.replace('window.qEvents', JSON.stringify(opts.events));
}

// default script selector behavior
return opts.debug ? QWIK_LOADER_DEFAULT_DEBUG : QWIK_LOADER_DEFAULT_MINIFIED;
}
Expand Down
1 change: 0 additions & 1 deletion packages/qwik/src/server/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,6 @@ export interface RenderResult {

/** @public */
export interface QwikLoaderOptions {
events?: string[];
include?: 'always' | 'never' | 'auto';
position?: 'top' | 'bottom';
}
Expand Down
91 changes: 16 additions & 75 deletions scripts/submodule-qwikloader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -98,92 +98,39 @@ export async function submoduleQwikLoader(config: BuildConfig) {
],
};

const optimizeMinified: OutputOptions = {
// QWIK_LOADER_OPTIMIZE_MINIFIED
dir: config.distQwikPkgDir,
format: 'es',
entryFileNames: `qwikloader.optimize.js`,
exports: 'none',
intro: `(()=>{`,
outro: `})()`,
plugins: [
terser({
compress: {
global_defs: {
'window.BuildEvents': true,
},
keep_fargs: false,
unsafe: true,
passes: 2,
},
format: {
comments: /@vite-ignore/g,
},
}),
],
};

const optimizeDebug: OutputOptions = {
// QWIK_LOADER_OPTIMIZE_DEBUG
dir: config.distQwikPkgDir,
format: 'es',
entryFileNames: `qwikloader.optimize.debug.js`,
exports: 'none',
intro: `(()=>{`,
outro: `})()`,
plugins: [
terser({
compress: {
global_defs: {
'window.BuildEvents': true,
},
inline: false,
join_vars: false,
loops: false,
sequences: false,
},
format: {
comments: /@vite-ignore/g,
beautify: true,
braces: true,
},
mangle: false,
}),
],
};

const build = await rollup(input);

await Promise.all([
build.write(defaultMinified),
build.write(defaultDebug),
build.write(optimizeMinified),
build.write(optimizeDebug),
]);
await Promise.all([build.write(defaultMinified), build.write(defaultDebug)]);

await generateLoaderSubmodule(config);

const optimizeFileSize = await fileSize(join(config.distQwikPkgDir, 'qwikloader.optimize.js'));
console.log(`🐸 qwikloader:`, optimizeFileSize);
const loaderSize = await fileSize(join(config.distQwikPkgDir, 'qwikloader.js'));
console.log(`🐸 qwikloader:`, loaderSize);
}

const getLoaderJsonString = async (config: BuildConfig, name: string) => {
const filePath = join(config.distQwikPkgDir, name);
const content = await readFile(filePath, 'utf-8');
// Remove vite comments and leading/trailing whitespace
let cleaned = content.trim().replace(/\n?\/\*\s*@vite[^*]+\*\/\n?/g, '');
if (cleaned.endsWith(';')) {
cleaned = cleaned.slice(0, -1);
}
return JSON.stringify(cleaned);
};
/** Load each of the qwik scripts to be inlined with esbuild "define" as const variables. */
export async function inlineQwikScriptsEsBuild(config: BuildConfig) {
const variableToFileMap = [
['QWIK_LOADER_DEFAULT_MINIFIED', 'qwikloader.js'],
['QWIK_LOADER_DEFAULT_DEBUG', 'qwikloader.debug.js'],
['QWIK_LOADER_OPTIMIZE_MINIFIED', 'qwikloader.optimize.js'],
['QWIK_LOADER_OPTIMIZE_DEBUG', 'qwikloader.optimize.debug.js'],
];

const define: { [varName: string]: string } = {};

await Promise.all(
variableToFileMap.map(async (varToFile) => {
const varName = `globalThis.${varToFile[0]}`;
const filePath = join(config.distQwikPkgDir, varToFile[1]);
const content = await readFile(filePath, 'utf-8');
define[varName] = JSON.stringify(content.trim());
define[varName] = await getLoaderJsonString(config, varToFile[1]);
})
);

Expand All @@ -193,15 +140,9 @@ export async function inlineQwikScriptsEsBuild(config: BuildConfig) {
async function generateLoaderSubmodule(config: BuildConfig) {
const loaderDistDir = join(config.distQwikPkgDir, 'loader');

const loaderCode = await readFile(join(config.distQwikPkgDir, 'qwikloader.js'), 'utf-8');
const loaderDebugCode = await readFile(
join(config.distQwikPkgDir, 'qwikloader.debug.js'),
'utf-8'
);

const code = [
`const QWIK_LOADER = ${JSON.stringify(loaderCode.trim())};`,
`const QWIK_LOADER_DEBUG = ${JSON.stringify(loaderDebugCode.trim())};`,
`const QWIK_LOADER = ${await getLoaderJsonString(config, 'qwikloader.js')};`,
`const QWIK_LOADER_DEBUG = ${await getLoaderJsonString(config, 'qwikloader.debug.js')};`,
];

const esmCode = [...code, `export { QWIK_LOADER, QWIK_LOADER_DEBUG };`];
Expand Down
1 change: 0 additions & 1 deletion starters/apps/e2e/src/entry.ssr.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@ export default function (opts: RenderToStreamOptions) {
qwikLoader: {
include:
url.searchParams.get("loader") === "false" ? "never" : "auto",
events: ["click"],
},
...opts,
},
Expand Down

0 comments on commit b05b7ae

Please sign in to comment.