Skip to content

Commit 3ea8442

Browse files
Merge branch 'QwikDev:main' into upgrade-eslint
2 parents 7dadacb + a67c3be commit 3ea8442

File tree

16 files changed

+270
-256
lines changed

16 files changed

+270
-256
lines changed

Diff for: .changeset/cyan-deers-glow.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@builder.io/qwik': patch
3+
---
4+
5+
FIX: When csr is true, it causes a crash because resolve cannot be null as the second parameter

Diff for: .changeset/wicked-dolphins-boil.md

+5
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'eslint-plugin-qwik': patch
3+
---
4+
5+
FIX: Enhance lexical scope and skip variables declared by Qwik's internal hooks.

Diff for: package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -194,7 +194,7 @@
194194
"api.update": "tsx --require ./scripts/runBefore.ts scripts/index.ts --tsc --api --dev",
195195
"build": "tsx --require ./scripts/runBefore.ts scripts/index.ts",
196196
"build.changelog-formatter": "tsc .changeset/changelog-github-custom.ts && mv .changeset/changelog-github-custom.js .changeset/changelog-github-custom.cjs",
197-
"build.clean": "rm -rf packages/qwik/dist/ && rm -rf packages/qwik-city/lib/ && rm -rf packages/docs/dist/ && rm -rf packages/insights/dist/ && rm -rf packages/qwik-labs/lib/ && rm -rf packages/qwik-labs/vite/",
197+
"build.clean": "tsx ./scripts/build-clean.ts",
198198
"build.cli": "tsx --require ./scripts/runBefore.ts scripts/index.ts --cli --dev",
199199
"build.cli.prod": "tsx --require ./scripts/runBefore.ts scripts/index.ts --cli",
200200
"build.core": "tsxs --require ./scripts/runBefore.ts scripts/index.ts --tsc --build --qwikcity --api --platform-binding",
@@ -217,7 +217,7 @@
217217
"cli.validate": "tsx --require ./scripts/runBefore.ts scripts/validate-cli.ts",
218218
"deps": "corepack pnpm upgrade -i -r --latest && syncpack fix-mismatches && corepack pnpm dedupe",
219219
"docs.dev": "pnpm -C packages/docs build.repl-sw && pnpm -C packages/docs dev",
220-
"docs.preview": "cd packages/docs && pnpm preview",
220+
"docs.preview": "pnpm -C packages/docs preview",
221221
"docs.sync": "tsx --require ./scripts/runBefore.ts scripts/docs_sync/index.ts && pnpm fmt",
222222
"eslint.update": "tsx --require ./scripts/runBefore.ts scripts/eslint-docs.ts",
223223
"fmt": "pnpm prettier.fix && pnpm syncpack format",

Diff for: packages/docs/package.json

+5-2
Original file line numberDiff line numberDiff line change
@@ -43,8 +43,6 @@
4343
"qwik-image": "0.0.10",
4444
"react": "18.3.1",
4545
"react-dom": "18.3.1",
46-
"rehype-pretty-code": "0.14.0",
47-
"shiki": "1.29.1",
4846
"snarkdown": "2.0.0",
4947
"tailwindcss": "3.4.6",
5048
"terser": "5.31.3",
@@ -54,6 +52,11 @@
5452
"valibot": "0.33.3",
5553
"vite": "5.3.5",
5654
"vite-plugin-inspect": "0.8.5",
55+
"shiki": "3.1.0",
56+
"@shikijs/rehype": "3.1.0",
57+
"@shikijs/transformers": "3.1.0",
58+
"@shikijs/colorized-brackets": "3.1.0",
59+
"@shikijs/types": "3.1.0",
5760
"wrangler": "3.65.1"
5861
},
5962
"engines": {

Diff for: packages/docs/src/components/code-sandbox/index.tsx

+1-6
Original file line numberDiff line numberDiff line change
@@ -30,12 +30,7 @@ export default component$<{
3030
))}
3131
</div>
3232
)}
33-
<div
34-
class="overflow-auto slot-container mb-4"
35-
style={{
36-
'--pretty-code-fragment-max-height': maxHeight ? maxHeight + 'px' : 'none',
37-
}}
38-
>
33+
<div class="overflow-auto slot-container mb-4">
3934
<Slot name={tabs ? String(activeTab.value) : ''} />
4035
</div>
4136
<div class="browser shadow-xl">

Diff for: packages/docs/src/routes/docs/docs.css

+12-16
Original file line numberDiff line numberDiff line change
@@ -185,29 +185,25 @@ h6 a:hover .icon {
185185
font-weight: 800;
186186
}
187187

188-
[data-rehype-pretty-code-fragment] {
189-
overflow: auto;
190-
color: white;
191-
background-color: rgb(1 31 51);
192-
border-radius: 8px;
193-
margin: 20px 0;
188+
.highlighted-word {
189+
background: #4199d3;
190+
color: #111 !important;
194191
}
195192

196-
[data-rehype-pretty-code-fragment] pre {
197-
overflow: auto;
198-
border-radius: 8px;
199-
max-height: var(--pretty-code-fragment-max-height);
193+
.highlighted {
194+
background-color: #7b8d9f33 !important;
200195
}
201196

202-
[data-rehype-pretty-code-title] {
203-
padding: 5px 15px;
197+
.shiki-title {
204198
background: #4199d3;
205-
font-family: monospace;
206-
font-weight: bold;
199+
color: #111;
200+
display: block;
201+
padding: 0.5rem;
202+
border-radius: 8px 8px 0px 0px;
207203
}
208204

209-
[data-highlighted-chars] {
210-
background: #ac7ef4;
205+
pre {
206+
border-radius: 8px;
211207
}
212208

213209
.docs article pre {

Diff for: packages/docs/vite.config.mts

+50-40
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,10 @@ import { defineConfig, loadEnv, type Plugin } from 'vite';
88
import Inspect from 'vite-plugin-inspect';
99
import { examplesData, playgroundData, rawSource, tutorialData } from './vite.repl-apps';
1010
import { sourceResolver } from './vite.source-resolver';
11+
import shikiRehype from '@shikijs/rehype';
12+
import { transformerMetaHighlight, transformerMetaWordHighlight } from '@shikijs/transformers';
13+
import { transformerColorizedBrackets } from '@shikijs/colorized-brackets';
14+
import type { ShikiTransformer } from '@shikijs/types';
1115

1216
const PUBLIC_QWIK_INSIGHTS_KEY = loadEnv('', '.', 'PUBLIC').PUBLIC_QWIK_INSIGHTS_KEY;
1317
const docsDir = new URL(import.meta.url).pathname;
@@ -52,9 +56,45 @@ const muteWarningsPlugin = (warningsToIgnore: string[][]): Plugin => {
5256
};
5357
};
5458

55-
export default defineConfig(async () => {
56-
const { default: rehypePrettyCode } = await import('rehype-pretty-code');
59+
function transformerShowEmptyLines(): ShikiTransformer {
60+
return {
61+
line(node) {
62+
if (node.children.length === 0) {
63+
node.children = [{ type: 'text', value: ' ' }];
64+
return node;
65+
}
66+
},
67+
};
68+
}
69+
70+
function transformerMetaShowTitle(): ShikiTransformer {
71+
return {
72+
root(node) {
73+
const meta = this.options.meta?.__raw;
74+
if (!meta) {
75+
return;
76+
}
77+
const titleMatch = meta.match(/title="([^"]*)"/);
78+
if (!titleMatch) {
79+
return;
80+
}
81+
const title = titleMatch[1] ?? '';
82+
if (title.length > 0) {
83+
node.children.unshift({
84+
type: 'element',
85+
tagName: 'div',
86+
properties: {
87+
class: 'shiki-title',
88+
},
89+
children: [{ type: 'text', value: title }],
90+
});
91+
}
92+
meta.replace(titleMatch[0], '');
93+
},
94+
};
95+
}
5796

97+
export default defineConfig(async () => {
5898
const routesDir = resolve('src', 'routes');
5999
return {
60100
dev: {
@@ -113,46 +153,16 @@ export default defineConfig(async () => {
113153
mdx: {
114154
rehypePlugins: [
115155
[
116-
rehypePrettyCode as any,
156+
shikiRehype,
117157
{
118158
theme: 'dark-plus',
119-
onVisitLine(node: any) {
120-
// Prevent lines from collapsing in `display: grid` mode, and
121-
// allow empty lines to be copy/pasted
122-
if (node.children.length === 0) {
123-
node.children = [{ type: 'text', value: ' ' }];
124-
}
125-
},
126-
onVisitHighlightedLine(node: any) {
127-
// Each line node by default has `class="line"`.
128-
if (node.properties.className) {
129-
node.properties.className.push('line--highlighted');
130-
}
131-
},
132-
onVisitHighlightedWord(node: any, id: string) {
133-
// Each word node has no className by default.
134-
node.properties.className = ['word'];
135-
if (id) {
136-
const backgroundColor = {
137-
a: 'rgb(196 42 94 / 59%)',
138-
b: 'rgb(0 103 163 / 56%)',
139-
c: 'rgb(100 50 255 / 35%)',
140-
}[id];
141-
142-
const color = {
143-
a: 'rgb(255 225 225 / 100%)',
144-
b: 'rgb(175 255 255 / 100%)',
145-
c: 'rgb(225 200 255 / 100%)',
146-
}[id];
147-
if (node.properties['data-rehype-pretty-code-wrapper']) {
148-
node.children.forEach((childNode: any) => {
149-
childNode.properties.style = ``;
150-
childNode.properties.className = '';
151-
});
152-
}
153-
node.properties.style = `background-color: ${backgroundColor}; color: ${color};`;
154-
}
155-
},
159+
transformers: [
160+
transformerMetaHighlight(),
161+
transformerMetaWordHighlight(),
162+
transformerColorizedBrackets(),
163+
transformerShowEmptyLines(),
164+
transformerMetaShowTitle(),
165+
],
156166
},
157167
],
158168
],

Diff for: packages/eslint-plugin-qwik/src/validLexicalScope.ts

+40
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,11 @@ export const validLexicalScope = createRule({
7474
if (variableType === 'ImportBinding') {
7575
return;
7676
}
77+
78+
if (isQwikHook(declaredVariable, context)) {
79+
return;
80+
}
81+
7782
let dollarScope: Scope.Scope | null = ref.from;
7883
let dollarIdentifier: string | undefined;
7984
while (dollarScope) {
@@ -477,6 +482,41 @@ function getContent(symbol: ts.Symbol, sourceCode: string) {
477482
return '';
478483
}
479484

485+
function isQwikHook(variable, context) {
486+
const def = variable.defs[0];
487+
if (!def || def.type !== 'Variable') {
488+
return false;
489+
}
490+
491+
const init = def.node.init;
492+
if (
493+
init?.type === 'CallExpression' &&
494+
init.callee.type === 'Identifier' &&
495+
/^use[A-Z]/.test(init.callee.name)
496+
) {
497+
const hookName = init.callee.name;
498+
const scope = context.sourceCode.getScope(def.node);
499+
const ref = scope.references.find((r) => r.identifier.name === hookName);
500+
501+
return ref?.resolved && isFromQwikModule(ref.resolved, context);
502+
}
503+
return false;
504+
}
505+
506+
function isFromQwikModule(resolvedVar) {
507+
return resolvedVar.defs.some((def) => {
508+
if (def.type !== 'ImportBinding') {
509+
return false;
510+
}
511+
const importSource = def.parent.source.value;
512+
513+
return (
514+
importSource.startsWith('@builder.io/qwik') ||
515+
importSource.startsWith('@builder.io/qwik-city')
516+
);
517+
});
518+
}
519+
480520
const ALLOWED_CLASSES = {
481521
Promise: true,
482522
URL: true,
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
import { component$, useComputed$ } from '@builder.io/qwik';
2+
import { useDocumentHead } from '@builder.io/qwik-city';
3+
import { useLocation as exampleTest } from '@builder.io/qwik-city';
4+
const loc = exampleTest();
5+
6+
export default component$(() => {
7+
const head = useDocumentHead();
8+
const authorId = useComputed$(() => {
9+
return head.meta; // <--- EESLint was not happy here, but now it is
10+
});
11+
return (
12+
<>
13+
{authorId.value}
14+
<p>pathname: {loc.url.pathname}</p>
15+
<p>skuId: {loc.params.skuId}</p>
16+
</>
17+
);
18+
});

Diff for: packages/qwik/src/optimizer/src/plugins/plugin.ts

+4-2
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,7 @@ export interface QwikPackages {
7777
path: string;
7878
}
7979

80-
export function createPlugin(optimizerOptions: OptimizerOptions = {}) {
80+
export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
8181
const id = `${Math.round(Math.random() * 899) + 100}`;
8282

8383
const clientResults = new Map<string, TransformOutput>();
@@ -102,7 +102,7 @@ export function createPlugin(optimizerOptions: OptimizerOptions = {}) {
102102
rootDir: null as any,
103103
tsconfigFileNames: ['./tsconfig.json'],
104104
input: null as any,
105-
outDir: null as any,
105+
outDir: '',
106106
assetsDir: null as any,
107107
resolveQwikBuild: true,
108108
entryStrategy: null as any,
@@ -1051,6 +1051,8 @@ export interface NormalizedQwikPluginOptions
10511051
experimental?: Record<keyof typeof ExperimentalFeatures, boolean>;
10521052
}
10531053

1054+
export type QwikPlugin = ReturnType<typeof createQwikPlugin>;
1055+
10541056
/** @public */
10551057
export type QwikBuildTarget = 'client' | 'ssr' | 'lib' | 'test';
10561058

Diff for: packages/qwik/src/optimizer/src/plugins/plugin.unit.ts

+8-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
import path, { resolve } from 'node:path';
22
import { assert, describe, expect, test } from 'vitest';
33
import type { QwikManifest } from '../types';
4-
import { ExperimentalFeatures, createPlugin } from './plugin';
4+
import { ExperimentalFeatures, createQwikPlugin } from './plugin';
55
import { normalizePath } from '../../../testing/util';
66
import { qwikVite } from './vite';
77

@@ -90,6 +90,12 @@ test('debug true', async () => {
9090
assert.deepEqual(opts.debug, true);
9191
});
9292

93+
test('csr', async () => {
94+
const plugin = await mockPlugin();
95+
const opts = plugin.normalizeOptions({ csr: true });
96+
assert.deepEqual(opts.outDir, '');
97+
});
98+
9399
test('override entryStrategy', async () => {
94100
const plugin = await mockPlugin();
95101
const opts = plugin.normalizeOptions({
@@ -291,7 +297,7 @@ describe('resolveId', () => {
291297
});
292298

293299
async function mockPlugin(os = process.platform) {
294-
const plugin = createPlugin({
300+
const plugin = createQwikPlugin({
295301
sys: {
296302
cwd: () => process.cwd(),
297303
env: 'node',

0 commit comments

Comments
 (0)