Skip to content

Commit

Permalink
Merge remote-tracking branch 'origin/main' into merge-main
Browse files Browse the repository at this point in the history
  • Loading branch information
wmertens committed Sep 24, 2024
2 parents 62c4c00 + 426ba59 commit 4f925f2
Show file tree
Hide file tree
Showing 13 changed files with 124 additions and 48 deletions.
5 changes: 5 additions & 0 deletions .changeset/quiet-grapes-sit.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@builder.io/qwik': patch
---

fix(optimizer): ignore unknown deps in graph causing crashes during build
6 changes: 2 additions & 4 deletions .github/workflows/deploy-docs.yml
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ jobs:
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v2
uses: actions/checkout@v4

- name: Download docs artifact
uses: actions/download-artifact@v4
Expand All @@ -38,9 +38,7 @@ jobs:

# not the official version, so be careful when updating
- name: Deploy to Cloudflare Pages
# if the docs didn't build, don't deploy but don't fail the workflow
if: success()
uses: AdrianGonz97/refined-cf-pages-action@f026b5a7c0cb4a05ecb63871c5fc4992cf036d7f
uses: AdrianGonz97/refined-cf-pages-action@6c0d47ff7c97c48fa702b6d9f71f7e3a7c30c7d8
with:
apiToken: ${{ secrets.CLOUDFLARE_API_TOKEN }}
accountId: ${{ secrets.CLOUDFLARE_ACCOUNT_ID }}
Expand Down
6 changes: 3 additions & 3 deletions e2e/qwik-cli-e2e/package.json
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
{
"name": "qwik-cli-e2e",
"dependencies": {
"kleur": "4.1.5"
},
"private": true,
"scripts": {
"e2e": "vitest run --config=vite.config.ts",
"e2e:watch": "vitest watch --config=vite.config.ts"
},
"dependencies": {
"kleur": "4.1.5"
}
}
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -233,6 +233,7 @@
"test": "pnpm build.full && pnpm test.unit && pnpm test.e2e",
"test.e2e-cli": "pnpm --filter qwik-cli-e2e e2e",
"test.e2e": "pnpm test.e2e.chromium && pnpm test.e2e.webkit",
"test.e2e-cli": "pnpm --filter qwik-cli-e2e e2e",
"test.e2e.chromium": "playwright test starters --browser=chromium --config starters/playwright.config.ts",
"test.e2e.chromium.debug": "PWDEBUG=1 playwright test starters --browser=chromium --config starters/playwright.config.ts",
"test.e2e.city": "playwright test starters/e2e/qwikcity --browser=chromium --config starters/playwright.config.ts",
Expand Down
17 changes: 12 additions & 5 deletions packages/docs/src/routes/docs/(qwik)/advanced/containers/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ contributors:
- mhevery
- bab2683
- mrhoodz
updated_at: '2023-08-23T23:06:42Z'
updated_at: '2023-08-23T23:06:41Z'
created_at: '2023-03-20T23:45:13Z'
---

Expand All @@ -19,7 +19,14 @@ created_at: '2023-03-20T23:45:13Z'
Every Qwik application is contained inside a element, usually the `<html>` element. This element becomes the container for the application. The container is the root element for the application and all components, state and events are contained within.

```html
<html q:container="paused" q:version="0.12.1" q:base="/build">
<html
q:container="paused"
q:version="1.9.0"
q:render="ssr"
q:base="/build/"
q:manifest-hash="ggb7b3"
q:instance="b0yf84vwuup"
>
...
</html>
```
Expand All @@ -39,7 +46,7 @@ renderToStream(<Root />, {
The code above will render the following HTML:

```html
<html lang="en" q:container="paused" q:version="0.12.1" q:base="/build">
<html lang="en" q:container="paused" q:version="1.9.0" q:base="/build/">
...
</html>
```
Expand Down Expand Up @@ -68,12 +75,12 @@ Since the runtime ensures isolation across containers, several containers can co
Containers can be nested in a tree and can communicate and share data. The inter-component communication requires that the components have well-defined boundaries, which we call container protocols.

```html
<html q:container="paused" q:version="0.12.1" q:base="/build">
<html q:container="paused" q:version="1.9.0" q:base="/build/">
<head>
<title>My Qwik Application</title>
</head>
<body>
<header q:container="resumed" q:version="0.11.1" q:base="https://server.a/build">
<header q:container="resumed" q:version="1.8.0" q:base="https://server.a/build">
<div>
<h1>This is a header form a container</h1>
</div>
Expand Down
15 changes: 11 additions & 4 deletions packages/docs/src/routes/docs/(qwik)/components/tasks/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -181,7 +181,14 @@ One unique aspect of Qwik, is that components are mounted only **once** across t

### `track()`

There are times when it is desirable to re-run a task when a component state changes. This is done by using the `track()` function. The `track()` function allows you to set up a dependency on a component's state when initially rendered on the server, and then re-execute the task when the state changes in the browser. The same task will never be executed twice on the server.
There are times when it is desirable to re-run a task when a component state changes. This is done by using the `track()` function. The `track()` function allows you to set up a dependency on a component's state when initially rendered on the server, and then re-execute the task when the state changes in the browser.

Track accepts signals, stores and functions. It returns the value of the signal, the store itself, or the function call result.

When you pass a store, the track will be called when the store gets or removes properties as well as mutations of properties.
Note that deep store updates don't mutate the store itself, so `store[item].count++` will not trigger an update. Instead, you should individually track each `store[item]` (which are also automatically created stores).

During SSR, each component will wait until all tasks are completed before outputting the HTML for that component. So a task can be called multiple times during SSR if the tracked state changes due to other tasks.

> **Note**: If all you want to do is compute a new state from an existing state synchronously, you should use [`useComputed$()`](/docs/components/state/#usecomputed) instead.
Expand All @@ -195,9 +202,9 @@ export default component$(() => {
const delayText = useSignal('');

useTask$(({ track }) => {
track(() => text.value);
const value = text.value;
const update = () => (delayText.value = value);
// Passing a signal directly is more efficient than using a function.
const newText = track(text);
const update = () => (delayText.value = newText);
isServer
? update() // don't delay on server render value as part of SSR
: delay(500).then(update); // Delay in browser
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -52,3 +52,54 @@ throw redirect(302, '/login');
If you do not provide a status code, Qwik City will default to a `302` Found status.

Read more about redirect status codes [here](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status#redirection_messages).

## Managing multiple redirects

In some cases, you may need to manage multiple redirects based on different conditions. For example, you might want to redirect users from old URLs to new URLs after a site restructure. Additionally, you may want editors in a CMS to manage these redirects as well. Here's one of the ways you can handle multiple redirects in Qwik:

```tsx title="src/routes/layout.tsx"
import { type RequestHandler } from "@builder.io/qwik-city";

export const onGet: RequestHandler = async ({ url, redirect }) => {
// qwik city request caching ...

// example external data source
async function fetchRules(): Promise<
{ source: string; destination: string; permanent: boolean }[]
> {
// Fetch data from a CMS or API, and add more rules as needed.
// Filter and map your data to make it easier to handle, as simulated here:
return [
{ source: "/old-path", destination: "/new-path", permanent: true },
{
source: "/another-old-path",
destination: "/another-new-path",
permanent: false,
},
];
}

const redirectRules = await fetchRules();
const redirectUrl = redirectRules.find((rule) => {
if (url.pathname.endsWith("/")) {
return rule.source + "/" === url.pathname;
}

return rule.source === url.pathname;
});

if (redirectUrl) {
throw redirect(redirectUrl.permanent ? 308 : 307, redirectUrl.destination);
}
};
```

> Note: This code does not include caching mechanisms. Fetching redirect rules from an external source on every request can lead to performance issues. It's recommended to implement caching to improve efficiency.
The above example demonstrates:

- Layouts: Grabbing data inside a [root layout's onGet handler](https://qwik.dev/docs/routing/#middleware-layouts).
- URL Matching: When a user requests a URL, the handler checks if it matches any source in the redirect rules.
- Redirect Execution: If a match is found, it redirects the user to the corresponding destination URL.
- HTTP Status Codes: Uses status code [308](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/308) for permanent redirects and [307](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/307) for temporary ones.
- Content Management Integration: Enables content editors to control redirects through external data sources like a CMS or API.
6 changes: 3 additions & 3 deletions packages/docs/src/routes/docs/integrations/authjs/index.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -286,7 +286,7 @@ GITHUB_SECRET=
AUTH_SECRET=
```

> *IMPORTANT*: Please read the Qwik documentation about [Environment Variables](/docs/env-variables/) to ensure you are using them safely. Many provider secrets should be kept secure and not exposed to the client/browser.
> *IMPORTANT*: Please read the Qwik documentation about [Environment Variables](/docs/guides/env-variables/) to ensure you are using them safely. Many provider secrets should be kept secure and not exposed to the client/browser.
4. The application is now ready to implement authentication using Auth.js.
5. Enjoy!
Expand All @@ -303,7 +303,7 @@ AUTH_SECRET=

```ts title="src/routes/plugin@auth.ts"
import { QwikAuth$ } from "@auth/qwik";
import GitHub from "@auth/qwik/providers/github";
import Credentials from "@auth/qwik/providers/credentials";

export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
() => ({
Expand Down Expand Up @@ -331,7 +331,7 @@ export const { onRequest, useSession, useSignIn, useSignOut } = QwikAuth$(
AUTH_SECRET=
```

> *IMPORTANT*: Please read the Qwik documentation about [Environment Variables](/docs/env-variables/) to ensure you are using them safely. Many provider secrets should be kept secure and not exposed to the client/browser.
> *IMPORTANT*: Please read the Qwik documentation about [Environment Variables](/docs/guides/env-variables/) to ensure you are using them safely. Many provider secrets should be kept secure and not exposed to the client/browser.
3. The application is now ready to implement authentication using Auth.js.
4. Enjoy!
Expand Down
1 change: 0 additions & 1 deletion packages/qwik-city/src/buildtime/vite/dev-server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import {
QDATA_JSON,
runQwikCity,
} from '../../middleware/request-handler/user-response';
import { matchRoute } from '../../runtime/src/route-matcher';
import { getMenuLoader } from '../../runtime/src/routing';
import type {
ActionInternal,
Expand Down
26 changes: 13 additions & 13 deletions packages/qwik/src/optimizer/core/src/parse.rs
Original file line number Diff line number Diff line change
Expand Up @@ -337,19 +337,19 @@ pub fn transform_code(config: TransformCodeOptions) -> Result<TransformOutput, a
cm: Lrc::clone(&source_map),
});

// print before transform, for debugging
// println!(
// "{}",
// emit_source_code(
// Lrc::clone(&source_map.clone()),
// None,
// &main_module.clone(),
// config.root_dir,
// false,
// )
// .unwrap()
// .0
// );
// print before transform, for debugging
// println!(
// "{}",
// emit_source_code(
// Lrc::clone(&source_map.clone()),
// None,
// &main_module.clone(),
// config.root_dir,
// false,
// )
// .unwrap()
// .0
// );
program = program.fold_with(&mut qwik_transform);

let mut treeshaker = Treeshaker::new();
Expand Down
26 changes: 15 additions & 11 deletions packages/qwik/src/optimizer/src/manifest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -261,16 +261,21 @@ export function generateManifestFromBundles(
const buildPath = path.resolve(opts.rootDir, opts.outDir, 'build');
const canonPath = (p: string) =>
path.relative(buildPath, path.resolve(opts.rootDir, opts.outDir, p));
const getBundleName = (name: string) => {
const bundle = outputBundles[name];
if (!bundle) {
console.warn(`Client manifest generation: skipping external import "${name}"`);
return;
}
return canonPath(bundle.fileName);
};
// We need to find our QRL exports
const qrlNames = new Set([...segments.map((h) => h.name)]);
for (const outputBundle of Object.values(outputBundles)) {
if (outputBundle.type !== 'chunk') {
continue;
}
const bundleFileName = path.relative(
buildPath,
path.resolve(opts.outDir, outputBundle.fileName)
);
const bundleFileName = canonPath(outputBundle.fileName);

const bundle: QwikBundle = {
size: outputBundle.code.length,
Expand All @@ -296,19 +301,18 @@ export function generateManifestFromBundles(
}

const bundleImports = outputBundle.imports
// Tree shaking can maybe remove imports
// Tree shaking might remove imports
.filter((i) => outputBundle.code.includes(path.basename(i)))
.map((i) => canonPath(outputBundles[i].fileName || i));
.map((i) => getBundleName(i))
.filter(Boolean) as string[];
if (bundleImports.length > 0) {
bundle.imports = bundleImports;
}

const bundleDynamicImports = outputBundle.dynamicImports
.filter(
// Tree shaking can remove dynamic imports
(i) => outputBundle.code.includes(path.basename(i))
)
.map((i) => canonPath(outputBundles[i].fileName || i));
.filter((i) => outputBundle.code.includes(path.basename(i)))
.map((i) => getBundleName(i))
.filter(Boolean) as string[];
if (bundleDynamicImports.length > 0) {
bundle.dynamicImports = bundleDynamicImports;
}
Expand Down
8 changes: 6 additions & 2 deletions packages/qwik/src/optimizer/src/plugins/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1129,6 +1129,10 @@ export function convertManifestToBundleGraph(manifest: QwikManifest): QwikBundle
const map = new Map<string, { index: number; deps: Set<string> }>();
const clearTransitiveDeps = (parentDeps: Set<string>, seen: Set<string>, bundleName: string) => {
const bundle = graph[bundleName];
if (!bundle) {
// external dependency
return;
}
for (const dep of bundle.imports || []) {
if (parentDeps.has(dep)) {
parentDeps.delete(dep);
Expand All @@ -1145,7 +1149,7 @@ export function convertManifestToBundleGraph(manifest: QwikManifest): QwikBundle
const deps = new Set(bundle.imports);
for (const depName of deps) {
if (!graph[depName]) {
// weird but ok
// external dependency
continue;
}
clearTransitiveDeps(deps, new Set(), depName);
Expand All @@ -1155,7 +1159,7 @@ export function convertManifestToBundleGraph(manifest: QwikManifest): QwikBundle
// If we dynamically import a qrl segment that is not a handler, we'll probably need it soon
const dep = graph[depName];
if (!graph[depName]) {
// weird but ok
// external dependency
continue;
}
if (dep.isTask) {
Expand Down
4 changes: 2 additions & 2 deletions packages/qwik/src/server/prefetch-strategy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -50,8 +50,8 @@ function getAutoPrefetch(qrls: QRL[], resolvedManifest: ResolvedManifest, buildB
const urls = new Map<string, PrefetchResource>();

if (mapper && manifest) {
for (const obj of qrls) {
const qrlSymbolName = obj.getHash();
for (const qrl of qrls) {
const qrlSymbolName = qrl.getHash();
const resolvedSymbol = mapper[qrlSymbolName];
if (resolvedSymbol) {
const bundleFileName = resolvedSymbol[1];
Expand Down

0 comments on commit 4f925f2

Please sign in to comment.