Skip to content

Commit 00c0bad

Browse files
committed
refactor(optimizer): input and q-manifest handling
- saner default input handling - do not write q-manifest to temp dir - try reading manifest on every platform that has the `node:fs` module
1 parent c9eaa78 commit 00c0bad

File tree

6 files changed

+157
-167
lines changed

6 files changed

+157
-167
lines changed

.changeset/short-suits-bet.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
'@qwik.dev/core': minor
3+
---
4+
5+
FIX: `qwikVite` has better vite config handling around input files, and no longer writes the q-manifest file to a temp dir.

packages/qwik/src/optimizer/src/plugins/plugin.ts

Lines changed: 90 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -115,7 +115,8 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
115115
resolveQwikBuild: true,
116116
entryStrategy: null as any,
117117
srcDir: null as any,
118-
srcInputs: null as any,
118+
ssrOutDir: '',
119+
clientOutDir: '',
119120
sourcemap: !!optimizerOptions.sourcemap,
120121
manifestInput: null,
121122
manifestOutput: null,
@@ -131,10 +132,16 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
131132
};
132133

133134
let lazyNormalizePath: (id: string) => string;
135+
let maybeFs: typeof import('fs') | undefined;
134136
const init = async () => {
135137
if (!internalOptimizer) {
136138
internalOptimizer = await createOptimizer(optimizerOptions);
137139
lazyNormalizePath = makeNormalizePath(internalOptimizer.sys);
140+
try {
141+
maybeFs = await internalOptimizer.sys.dynamicImport('node:fs');
142+
} catch {
143+
// ignore
144+
}
138145
}
139146
};
140147

@@ -161,7 +168,9 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
161168
};
162169

163170
/** Note that as a side-effect this updates the internal plugin `opts` */
164-
const normalizeOptions = (inputOpts?: QwikPluginOptions) => {
171+
const normalizeOptions = async (
172+
inputOpts?: QwikPluginOptions
173+
): Promise<NormalizedQwikPluginOptions> => {
165174
const updatedOpts: QwikPluginOptions = Object.assign({}, inputOpts);
166175

167176
const optimizer = getOptimizer();
@@ -229,45 +238,72 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
229238
opts.tsconfigFileNames = updatedOpts.tsconfigFileNames;
230239
}
231240

232-
if (!updatedOpts.csr) {
233-
if (!updatedOpts.input) {
234-
if (opts.target === 'ssr') {
235-
// ssr input default - should actually never be used
236-
const ssrInput = path.resolve(srcDir, 'entry.ssr');
237-
opts.input = [ssrInput];
238-
} else if (opts.target === 'client') {
239-
// client input default
240-
const clientInput = path.resolve(srcDir, 'root');
241-
opts.input = [clientInput];
242-
} else if (opts.target === 'lib') {
243-
// lib input default
244-
const libInput = path.resolve(srcDir, 'index.ts');
245-
opts.input = [libInput];
246-
} else {
247-
opts.input = undefined!;
248-
}
241+
if (!updatedOpts.input && !opts.input) {
242+
// we only provide inputs if none were provided by the user
243+
if (opts.target === 'ssr') {
244+
// this is for dev mode, prod will have own setting
245+
opts.input = [path.resolve(srcDir, 'entry.ssr')];
246+
} else if (opts.target === 'client') {
247+
// not really an entry, just a starting point
248+
opts.input = [path.resolve(srcDir, 'root')];
249+
} else {
250+
// others including lib should be ok already
251+
opts.input = undefined!;
249252
}
253+
}
250254

251-
if (typeof updatedOpts.outDir === 'string') {
252-
opts.outDir = normalizePath(path.resolve(opts.rootDir, normalizePath(updatedOpts.outDir)));
253-
} else if (!opts.outDir) {
254-
if (opts.target === 'ssr') {
255-
opts.outDir = normalizePath(path.resolve(opts.rootDir, SSR_OUT_DIR));
256-
} else if (opts.target === 'lib') {
257-
opts.outDir = normalizePath(path.resolve(opts.rootDir, LIB_OUT_DIR));
258-
} else {
259-
opts.outDir = normalizePath(path.resolve(opts.rootDir, CLIENT_OUT_DIR));
260-
}
261-
}
255+
if (updatedOpts.outDir) {
256+
// forced output directory
257+
opts.outDir = normalizePath(path.resolve(opts.rootDir, normalizePath(updatedOpts.outDir)));
258+
}
259+
260+
// default output directory
261+
opts.clientOutDir = normalizePath(
262+
path.resolve(opts.rootDir, updatedOpts.clientOutDir || CLIENT_OUT_DIR)
263+
);
264+
opts.ssrOutDir = normalizePath(
265+
path.resolve(opts.rootDir, updatedOpts.ssrOutDir || SSR_OUT_DIR)
266+
);
267+
if (opts.target === 'ssr') {
268+
// server
269+
opts.outDir ||= opts.ssrOutDir;
270+
} else if (opts.target === 'lib') {
271+
// library
272+
opts.outDir ||= normalizePath(path.resolve(opts.rootDir, LIB_OUT_DIR));
273+
} else {
274+
// client
275+
opts.outDir ||= opts.clientOutDir;
262276
}
263277

264278
if (typeof updatedOpts.manifestOutput === 'function') {
265279
opts.manifestOutput = updatedOpts.manifestOutput;
266280
}
267281

268-
const clientManifest = getValidManifest(updatedOpts.manifestInput);
269-
if (clientManifest) {
270-
opts.manifestInput = clientManifest;
282+
if (updatedOpts.manifestInput) {
283+
opts.manifestInput = getValidManifest(updatedOpts.manifestInput) || null;
284+
}
285+
if (
286+
!opts.manifestInput &&
287+
opts.target === 'ssr' &&
288+
opts.buildMode === 'production' &&
289+
maybeFs
290+
) {
291+
let clientManifestPath = normalizePath(path.resolve(opts.clientOutDir, Q_MANIFEST_FILENAME));
292+
if (!(await maybeFs.promises.stat(clientManifestPath).catch(() => false))) {
293+
clientManifestPath = normalizePath(
294+
path.resolve(opts.rootDir, CLIENT_OUT_DIR, Q_MANIFEST_FILENAME)
295+
);
296+
}
297+
try {
298+
const clientManifestStr = await maybeFs.promises.readFile(clientManifestPath, 'utf-8');
299+
opts.manifestInput = getValidManifest(JSON.parse(clientManifestStr)) || null;
300+
// eslint-disable-next-line no-console
301+
console.info('Read client manifest from', clientManifestPath);
302+
} catch (e) {
303+
console.warn(
304+
`could not read Qwik client manifest ${clientManifestPath}, ignoring. Make sure you provide it to the SSR renderer. (${e})`
305+
);
306+
}
271307
}
272308

273309
if (typeof updatedOpts.transformedModuleOutput === 'function') {
@@ -276,6 +312,18 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
276312

277313
if (updatedOpts.scope !== undefined) {
278314
opts.scope = updatedOpts.scope;
315+
} else if (!opts.scope && maybeFs) {
316+
// Use the package name for the scope
317+
const pkgPath = path.resolve(opts.rootDir, 'package.json');
318+
try {
319+
const pkgString = await maybeFs.promises.readFile(pkgPath, 'utf-8');
320+
const pkg = JSON.parse(pkgString);
321+
if (typeof pkg.name === 'string') {
322+
opts.scope = pkg.name;
323+
}
324+
} catch (e) {
325+
console.warn(`could not read ${pkgPath}, ignoring. (${e})`);
326+
}
279327
}
280328

281329
if (typeof updatedOpts.resolveQwikBuild === 'boolean') {
@@ -867,6 +915,7 @@ export function createQwikPlugin(optimizerOptions: OptimizerOptions = {}) {
867915
diagnosticsCallback = cb;
868916
};
869917

918+
/** Convert windows backslashes to forward slashes if possible */
870919
const normalizePath = (id: string) => lazyNormalizePath(id);
871920

872921
function getQwikBuildModule(isServer: boolean, _target: QwikBuildTarget) {
@@ -1005,9 +1054,6 @@ export const manifest = ${JSON.stringify(serverManifest)};\n`;
10051054
if (typeof opts.transformedModuleOutput === 'function') {
10061055
await opts.transformedModuleOutput(getTransformedOutputs());
10071056
}
1008-
1009-
// TODO get rid of this with the vite environment api
1010-
return manifestStr;
10111057
}
10121058

10131059
return {
@@ -1123,6 +1169,8 @@ export interface QwikPluginOptions {
11231169
manifestInput?: QwikManifest | null;
11241170
input?: string[] | string | { [entry: string]: string };
11251171
outDir?: string;
1172+
ssrOutDir?: string;
1173+
clientOutDir?: string;
11261174
assetsDir?: string;
11271175
srcDir?: string | null;
11281176
scope?: string | null;
@@ -1154,9 +1202,12 @@ export interface QwikPluginOptions {
11541202
}
11551203

11561204
export interface NormalizedQwikPluginOptions
1157-
extends Omit<Required<QwikPluginOptions>, 'vendorRoots' | 'experimental'> {
1158-
input: string[] | { [entry: string]: string };
1159-
experimental?: Record<keyof typeof ExperimentalFeatures, boolean>;
1205+
extends Omit<
1206+
Required<QwikPluginOptions>,
1207+
'input' | 'vendorRoots' | 'srcInputs' | 'experimental'
1208+
> {
1209+
input: string[] | { [entry: string]: string } | undefined;
1210+
experimental: Record<keyof typeof ExperimentalFeatures, boolean> | undefined;
11601211
}
11611212

11621213
export type QwikPlugin = ReturnType<typeof createQwikPlugin>;

packages/qwik/src/optimizer/src/plugins/plugin.unit.ts

Lines changed: 22 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ test('types', () => () => {
1515

1616
test('defaults', async () => {
1717
const plugin = await mockPlugin();
18-
const opts = plugin.normalizeOptions();
18+
const opts = await plugin.normalizeOptions();
1919
assert.deepEqual(opts.target, 'client');
2020
assert.deepEqual(opts.buildMode, 'development');
2121
assert.deepEqual(opts.entryStrategy, { type: 'segment' });
@@ -31,7 +31,7 @@ test('defaults', async () => {
3131

3232
test('defaults (buildMode: production)', async () => {
3333
const plugin = await mockPlugin();
34-
const opts = plugin.normalizeOptions({ buildMode: 'production' });
34+
const opts = await plugin.normalizeOptions({ buildMode: 'production' });
3535
assert.deepEqual(opts.target, 'client');
3636
assert.deepEqual(opts.buildMode, 'production');
3737
assert.deepEqual(opts.entryStrategy, { type: 'smart' });
@@ -49,7 +49,7 @@ test('defaults (buildMode: production)', async () => {
4949

5050
test('defaults (target: ssr)', async () => {
5151
const plugin = await mockPlugin();
52-
const opts = plugin.normalizeOptions({ target: 'ssr' });
52+
const opts = await plugin.normalizeOptions({ target: 'ssr' });
5353
assert.deepEqual(opts.target, 'ssr');
5454
assert.deepEqual(opts.buildMode, 'development');
5555
assert.deepEqual(opts.entryStrategy, { type: 'hoist' });
@@ -66,7 +66,7 @@ test('defaults (target: ssr)', async () => {
6666

6767
test('defaults (buildMode: production, target: ssr)', async () => {
6868
const plugin = await mockPlugin();
69-
const opts = plugin.normalizeOptions({ buildMode: 'production', target: 'ssr' });
69+
const opts = await plugin.normalizeOptions({ buildMode: 'production', target: 'ssr' });
7070
assert.deepEqual(opts.target, 'ssr');
7171
assert.deepEqual(opts.buildMode, 'production');
7272
assert.deepEqual(opts.entryStrategy, { type: 'hoist' });
@@ -83,19 +83,19 @@ test('defaults (buildMode: production, target: ssr)', async () => {
8383

8484
test('debug true', async () => {
8585
const plugin = await mockPlugin();
86-
const opts = plugin.normalizeOptions({ debug: true });
86+
const opts = await plugin.normalizeOptions({ debug: true });
8787
assert.deepEqual(opts.debug, true);
8888
});
8989

9090
test('csr', async () => {
9191
const plugin = await mockPlugin();
92-
const opts = plugin.normalizeOptions({ csr: true });
92+
const opts = await plugin.normalizeOptions({ csr: true });
9393
assert.deepEqual(opts.outDir, '');
9494
});
9595

9696
test('override entryStrategy', async () => {
9797
const plugin = await mockPlugin();
98-
const opts = plugin.normalizeOptions({
98+
const opts = await plugin.normalizeOptions({
9999
entryStrategy: { type: 'component' },
100100
buildMode: 'production',
101101
});
@@ -104,21 +104,21 @@ test('override entryStrategy', async () => {
104104

105105
test('entryStrategy, smart', async () => {
106106
const plugin = await mockPlugin();
107-
const opts = plugin.normalizeOptions({
107+
const opts = await plugin.normalizeOptions({
108108
entryStrategy: { type: 'smart' },
109109
});
110110
assert.deepEqual(opts.entryStrategy.type, 'smart');
111111
});
112112

113113
test('entryStrategy, segment no forceFullBuild', async () => {
114114
const plugin = await mockPlugin();
115-
const opts = plugin.normalizeOptions({ entryStrategy: { type: 'segment' } });
115+
const opts = await plugin.normalizeOptions({ entryStrategy: { type: 'segment' } });
116116
assert.deepEqual(opts.entryStrategy.type, 'segment');
117117
});
118118

119119
test('entryStrategy, segment and srcInputs', async () => {
120120
const plugin = await mockPlugin();
121-
const opts = plugin.normalizeOptions({
121+
const opts = await plugin.normalizeOptions({
122122
entryStrategy: { type: 'segment' },
123123
});
124124
assert.deepEqual(opts.entryStrategy.type, 'segment');
@@ -127,42 +127,42 @@ test('entryStrategy, segment and srcInputs', async () => {
127127
test('rootDir, abs path', async () => {
128128
const plugin = await mockPlugin();
129129
const customRoot = normalizePath(resolve(cwd, 'abs-path'));
130-
const opts = plugin.normalizeOptions({ rootDir: customRoot });
130+
const opts = await plugin.normalizeOptions({ rootDir: customRoot });
131131
assert.deepEqual(opts.rootDir, customRoot);
132132
});
133133

134134
test('rootDir, rel path', async () => {
135135
const plugin = await mockPlugin();
136136
const customRoot = 'rel-path';
137-
const opts = plugin.normalizeOptions({ rootDir: customRoot });
137+
const opts = await plugin.normalizeOptions({ rootDir: customRoot });
138138
assert.deepEqual(opts.rootDir, normalizePath(resolve(cwd, customRoot)));
139139
});
140140

141141
test('tsconfigFileNames', async () => {
142142
const plugin = await mockPlugin();
143-
const opts = plugin.normalizeOptions({
143+
const opts = await plugin.normalizeOptions({
144144
tsconfigFileNames: ['./tsconfig.json', './tsconfig.app.json'],
145145
});
146146
assert.deepEqual(opts.tsconfigFileNames, ['./tsconfig.json', './tsconfig.app.json']);
147147
});
148148

149149
test('tsconfigFileNames, empty array fallback to default', async () => {
150150
const plugin = await mockPlugin();
151-
const opts = plugin.normalizeOptions({
151+
const opts = await plugin.normalizeOptions({
152152
tsconfigFileNames: [],
153153
});
154154
assert.deepEqual(opts.tsconfigFileNames, ['./tsconfig.json']);
155155
});
156156

157157
test('input string', async () => {
158158
const plugin = await mockPlugin();
159-
const opts = plugin.normalizeOptions({ input: 'src/cmps/main.tsx' });
159+
const opts = await plugin.normalizeOptions({ input: 'src/cmps/main.tsx' });
160160
assert.deepEqual(opts.input, [normalizePath(resolve(cwd, 'src', 'cmps', 'main.tsx'))]);
161161
});
162162

163163
test('input array', async () => {
164164
const plugin = await mockPlugin();
165-
const opts = plugin.normalizeOptions({
165+
const opts = await plugin.normalizeOptions({
166166
input: ['src/cmps/a.tsx', 'src/cmps/b.tsx'],
167167
});
168168
assert.deepEqual(opts.input, [
@@ -173,14 +173,14 @@ test('input array', async () => {
173173

174174
test('outDir', async () => {
175175
const plugin = await mockPlugin();
176-
const opts = plugin.normalizeOptions({ outDir: 'out' });
176+
const opts = await plugin.normalizeOptions({ outDir: 'out' });
177177
assert.deepEqual(opts.outDir, normalizePath(resolve(cwd, 'out')));
178178
});
179179

180180
test('manifestOutput', async () => {
181181
const plugin = await mockPlugin();
182182
const manifestOutput = () => {};
183-
const opts = plugin.normalizeOptions({ manifestOutput });
183+
const opts = await plugin.normalizeOptions({ manifestOutput });
184184
assert.deepEqual(opts.manifestOutput, manifestOutput);
185185
});
186186

@@ -193,19 +193,19 @@ test('manifestInput', async () => {
193193
bundles: {},
194194
version: '1',
195195
};
196-
const opts = plugin.normalizeOptions({ manifestInput });
196+
const opts = await plugin.normalizeOptions({ manifestInput });
197197
assert.deepEqual(opts.manifestInput, manifestInput);
198198
});
199199

200200
test('resolveQwikBuild true', async () => {
201201
const plugin = await mockPlugin();
202-
const opts = plugin.normalizeOptions({ resolveQwikBuild: true });
202+
const opts = await plugin.normalizeOptions({ resolveQwikBuild: true });
203203
assert.deepEqual(opts.resolveQwikBuild, true);
204204
});
205205

206206
test('resolveQwikBuild false', async () => {
207207
const plugin = await mockPlugin();
208-
const opts = plugin.normalizeOptions({ resolveQwikBuild: false });
208+
const opts = await plugin.normalizeOptions({ resolveQwikBuild: false });
209209
assert.deepEqual(opts.resolveQwikBuild, false);
210210
});
211211

@@ -216,7 +216,7 @@ test('experimental[]', async () => {
216216
// we can't test this without a flag
217217
return;
218218
}
219-
const opts = plugin.normalizeOptions({ experimental: [flag] });
219+
const opts = await plugin.normalizeOptions({ experimental: [flag] });
220220
assert.deepEqual(opts.experimental, { [flag]: true } as any);
221221
});
222222

0 commit comments

Comments
 (0)