Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

vite fix custom base url #2046

Open
wants to merge 4 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions packages/compat/src/compat-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -459,7 +459,7 @@ export default class CompatApp {
// internal implementation detail, and respecting outputPaths here is
// unnecessary complexity. The corresponding code that adjusts the HTML
// <link> is in updateHTML in app.ts.
outputPaths: { app: `/assets/${this.name}.css` },
outputPaths: { app: `/@embroider/virtual/app.css` },
registry: this.legacyEmberAppInstance.registry,
minifyCSS: this.legacyEmberAppInstance.options.minifyCSS.options,
};
Expand All @@ -473,10 +473,10 @@ export default class CompatApp {
version: 2,
'public-assets': {},
};
let assetPath = join(outputPath, 'assets');
let assetPath = join(outputPath, '@embroider', 'virtual');
if (pathExistsSync(assetPath)) {
for (let file of walkSync(assetPath, { directories: false })) {
addonMeta['public-assets']![`./assets/${file}`] = `/assets/${file}`;
addonMeta['public-assets']![`./@embroider/virtual/${file}`] = `/@embroider/virtual/${file}`;
}
}
let meta: PackageInfo = {
Expand Down
5 changes: 3 additions & 2 deletions packages/vite/src/assets.ts
Original file line number Diff line number Diff line change
Expand Up @@ -43,8 +43,9 @@ export function assets(): Plugin {
configureServer(server) {
return () => {
server.middlewares.use((req, res, next) => {
if (req.originalUrl && req.originalUrl.length > 1) {
const assetUrl = findPublicAsset(req.originalUrl.split('?')[0], resolverLoader.resolver);
const originalUrl = req.originalUrl!.slice((server.config.base.length || 1) - 1);
if (originalUrl && originalUrl.length > 1) {
const assetUrl = findPublicAsset(originalUrl.split('?')[0], resolverLoader.resolver);
if (assetUrl) {
return send(req, assetUrl).pipe(res as unknown as NodeJS.WritableStream);
}
Expand Down
2 changes: 1 addition & 1 deletion packages/vite/src/esbuild-request.ts
Original file line number Diff line number Diff line change
Expand Up @@ -112,7 +112,7 @@ export class EsBuildRequestAdapter implements RequestAdapter<Resolution<OnResolv
let pkg = this.packageCache.ownerOfFile(result.path);
if (
pkg?.root === this.packageCache.appRoot &&
// vite provides node built-in polyfills under a custom namespace and we dont
// vite provides node built-in polyfills under a custom namespace and we dont
// want to interrupt that. We'd prefer they get bundled in the dep optimizer normally,
// rather than getting deferred to the app build (which also works, but means they didn't
// get pre-optimized).
Expand Down
16 changes: 14 additions & 2 deletions packages/vite/src/scripts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,11 +26,14 @@ export function scripts(params?: { include?: string[]; exclude?: string[] }): Pl
}
});

let config: any = null;

return {
name: 'embroider-scripts',
enforce: 'pre',

configResolved(resolvedConfig) {
config = resolvedConfig;
optimizer = new ScriptOptimizer(resolvedConfig.root);
},

Expand All @@ -47,7 +50,7 @@ export function scripts(params?: { include?: string[]; exclude?: string[] }): Pl
// we don't do anything in `vite dev`, we only need to work in `vite
// build`
if (!context.server) {
return optimizer.transformHTML(htmlIn);
return optimizer.transformHTML(htmlIn, config.base);
}
},
};
Expand Down Expand Up @@ -123,16 +126,25 @@ class ScriptOptimizer {
return fileParts.join('.');
}

transformHTML(htmlIn: string) {
transformHTML(htmlIn: string, baseUrl: string) {
if (this.transformState?.htmlIn !== htmlIn) {
let parsed = new JSDOM(htmlIn);
let scriptTags = [...parsed.window.document.querySelectorAll('script')] as HTMLScriptElement[];
let linkTags = [...parsed.window.document.querySelectorAll('link')] as HTMLLinkElement[];
for (const linkTag of linkTags) {
if (linkTag.href.startsWith('/@embroider/virtual')) {
linkTag.href = baseUrl + linkTag.href.slice(1);
}
}
for (let scriptTag of scriptTags) {
if (scriptTag.type !== 'module') {
let fingerprinted = this.emitted.get(scriptTag.src);
if (fingerprinted) {
scriptTag.src = fingerprinted;
}
if (scriptTag.src.startsWith('/@embroider/virtual')) {
scriptTag.src = baseUrl + scriptTag.src.slice(1);
}
}
}
let htmlOut = parsed.serialize();
Expand Down
7 changes: 4 additions & 3 deletions test-packages/support/testem-proxy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import type { Application } from 'express';
"/tests/index.html" URL.
*/

export function testemProxy(targetURL: string) {
export function testemProxy(targetURL: string, base = '/') {
return function testemProxyHandler(app: Application) {
const proxy = httpProxy.createProxyServer({
changeOrigin: true,
Expand All @@ -23,10 +23,11 @@ export function testemProxy(targetURL: string) {

app.all('*', (req, res, next) => {
let url = req.url;
if (url === '/testem.js' || url.startsWith('/testem/')) {
if (url === `${base}testem.js` || url.startsWith('/testem/')) {
req.url = req.url.replace(base, '/');
return next();
}
let m = /^(\/\d+)\/tests($|.)+/.exec(url);
let m = /^(\/\d+).*\/tests($|.)+/.exec(url);
if (m) {
url = url.slice(m[1].length);
}
Expand Down
2 changes: 1 addition & 1 deletion tests/app-template/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{{content-for "head"}}

<link integrity="" rel="stylesheet" href="/@embroider/virtual/vendor.css">
<link integrity="" rel="stylesheet" href="/assets/app-template.css">
<link integrity="" rel="stylesheet" href="/@embroider/virtual/app.css">

{{content-for "head-footer"}}
</head>
Expand Down
2 changes: 1 addition & 1 deletion tests/app-template/tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{{content-for "head"}} {{content-for "test-head"}}

<link rel="stylesheet" href="/@embroider/virtual/vendor.css" />
<link rel="stylesheet" href="/assets/app-template.css" />
<link rel="stylesheet" href="/@embroider/virtual/app.css" />
<link rel="stylesheet" href="/@embroider/virtual/test-support.css" />

{{content-for "head-footer"}} {{content-for "test-head-footer"}}
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/macro-test/tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
{{content-for "test-head"}}

<link rel="stylesheet" href="/@embroider/virtual/vendor.css">
<link rel="stylesheet" href="/assets/app-template.css">
<link rel="stylesheet" href="/@embroider/virtual/app.css">
<link rel="stylesheet" href="/@embroider/virtual/test-support.css">

{{content-for "head-footer"}}
Expand Down
2 changes: 1 addition & 1 deletion tests/fixtures/preprocess-addon/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ module.exports = {
let relativePathWithPrefix = `/${relativePath}`;

if (relativePathWithPrefix === `${inputPath}/app.css`) {
return join(outputPath, 'app-template.css');
return join(outputPath, '../@embroider/virtual/app.css');
}

return join(outputPath, relativePathWithPrefix.replace(inputPath, ''));
Expand Down
2 changes: 1 addition & 1 deletion tests/scenarios/compat-addon-classic-features-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -72,7 +72,7 @@ appScenarios
{{content-for "head"}}
<link integrity="" rel="stylesheet" href="/@embroider/virtual/vendor.css">
<link integrity="" rel="stylesheet" href="/assets/app-template.css">
<link integrity="" rel="stylesheet" href="/@embroider/virtual/app.css">
{{content-for "head-footer"}}
</head>
Expand Down
4 changes: 2 additions & 2 deletions tests/scenarios/preprocess-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,15 +39,15 @@ appScenarios
test(`css is transformed: build mode`, async function (assert) {
let result = await app.execute(`pnpm build`);
assert.strictEqual(result.exitCode, 0, result.output);
let text = readFileSync(join(app.dir, `dist/assets/app-template.css`), 'utf8');
let text = readFileSync(join(app.dir, `dist/@embroider/virtual/app.css`), 'utf8');
assert.strictEqual(text, 'body { background: red; }');
});

test(`css is transformed: dev mode`, async function (assert) {
const server = CommandWatcher.launch('vite', ['--clearScreen', 'false'], { cwd: app.dir });
try {
const [, url] = await server.waitFor(/Local:\s+(https?:\/\/.*)\//g);
let response = await fetch(`${url}/assets/app-template.css`);
let response = await fetch(`${url}/@embroider/virtual/app.css`);
let text = await response.text();
assert.strictEqual(text, 'body { background: red; }');
} finally {
Expand Down
2 changes: 1 addition & 1 deletion tests/scenarios/vite-app-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -307,7 +307,7 @@ appScenarios
assert.ok(distFiles.includes('assets'), 'should have created assets folder');
assert.ok(distFiles.includes('robots.txt'), 'should have copied app assets');

const assetFiles = readdirSync(join(app.dir, 'dist', 'assets'));
const assetFiles = readdirSync(join(app.dir, 'dist', '@embroider', 'virtual'));
assert.ok(assetFiles.length > 1, 'should have created asset files');
});
});
Expand Down
146 changes: 146 additions & 0 deletions tests/scenarios/vite-internals-test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import QUnit from 'qunit';
import fetch from 'node-fetch';
import CommandWatcher from './helpers/command-watcher';
import { setupAuditTest } from '@embroider/test-support/audit-assertions';
import { mkdirSync, moveSync, readFileSync, writeFileSync } from 'fs-extra';
import { resolve } from 'path';

const { module: Qmodule, test } = QUnit;

Expand Down Expand Up @@ -354,3 +356,147 @@ tsAppScenarios
buildViteInternalsTest(false, app);
})
.forEachScenario(runViteInternalsTest);

tsAppScenarios
.map('vite-with-base-internals', app => {
// These are for a custom testem setup that will let us do runtime tests
// inside `vite dev` rather than only against the output of `vite build`.
//
// Most apps should run their CI against `vite build`, as that's closer to
// production. And they can do development tests directly in brower against
// `vite dev` at `/tests/index.html`. We're doing `vite dev` in CI here
// because we're testing the development experience itself.
app.linkDevDependency('testem', { baseDir: __dirname });
app.linkDevDependency('@embroider/test-support', { baseDir: __dirname });

app.linkDevDependency('ember-page-title', { baseDir: __dirname });
app.linkDevDependency('ember-welcome-page', { baseDir: __dirname });
const customBase = '/sub-dir/';
app.mergeFiles({
'testem-dev.js': `
'use strict';

module.exports = {
test_page: '${customBase}tests/index.html?hidepassed',
disable_watching: true,
launch_in_ci: ['Chrome'],
launch_in_dev: ['Chrome'],
browser_start_timeout: 120,
browser_args: {
Chrome: {
ci: [
// --no-sandbox is needed when running Chrome inside a container
process.env.CI ? '--no-sandbox' : null,
'--headless',
'--disable-dev-shm-usage',
'--disable-software-rasterizer',
'--mute-audio',
'--remote-debugging-port=0',
'--window-size=1440,900',
].filter(Boolean),
},
},
middleware: [
require('@embroider/test-support/testem-proxy').testemProxy('http://localhost:4200', '${customBase}')
],
};
`,

config: {
'environment.js': `
'use strict';

module.exports = function (environment) {
const ENV = {
modulePrefix: 'ts-app-template',
environment,
rootURL: '${customBase}',
locationType: 'history',
EmberENV: {
EXTEND_PROTOTYPES: false,
FEATURES: {
// Here you can enable experimental features on an ember canary build
// e.g. EMBER_NATIVE_DECORATOR_SUPPORT: true
},
},

APP: {
// Here you can pass flags/options to your application instance
// when it is created
},
};

if (environment === 'development') {
// ENV.APP.LOG_RESOLVER = true;
// ENV.APP.LOG_ACTIVE_GENERATION = true;
// ENV.APP.LOG_TRANSITIONS = true;
// ENV.APP.LOG_TRANSITIONS_INTERNAL = true;
// ENV.APP.LOG_VIEW_LOOKUPS = true;
}

if (environment === 'test') {
// Testem prefers this...
ENV.locationType = 'none';

// keep test console output quieter
ENV.APP.LOG_ACTIVE_GENERATION = false;
ENV.APP.LOG_VIEW_LOOKUPS = false;

ENV.APP.rootElement = '#ember-testing';
ENV.APP.autoboot = false;
}

if (environment === 'production') {
// here you can enable a production-specific feature
}

return ENV;
};
`,
},
});
})
.forEachScenario(runViteInternalsTestWithBase);

function runViteInternalsTestWithBase(scenario: Scenario) {
Qmodule(scenario.name, function (hooks) {
let app: PreparedApp;
let server: CommandWatcher;

hooks.before(async () => {
app = await scenario.prepare();
});

Qmodule('vite dev', function (hooks) {
hooks.before(async () => {
server = CommandWatcher.launch('vite', ['--clearScreen', 'false', '--base', '/sub-dir/'], { cwd: app.dir });
const [, appURL] = await server.waitFor(/Local:\s+(https?:\/\/.*)\//g);
let testem = readFileSync(resolve(app.dir, 'testem-dev.js')).toString();
testem = testem.replace('http://localhost:4200', appURL.replace('/sub-dir', ''));
writeFileSync(resolve(app.dir, 'testem-dev.js'), testem);
});

hooks.after(async () => {
await server?.shutdown();
});

test('run test suite against vite dev', async function (assert) {
let result = await app.execute('pnpm testem --file testem-dev.js ci');
assert.equal(result.exitCode, 0, result.output);
});
});

Qmodule('vite build', function (hooks) {
hooks.before(async () => {
await app.execute('pnpm vite build --mode test --base /sub-dir/');
mkdirSync(resolve(app.dir, './custom-base/sub-dir'), { recursive: true });
moveSync(resolve(app.dir, './dist'), resolve(app.dir, './custom-base/sub-dir'), { overwrite: true });
});

test('run test suite against vite dist with sub-dir', async function (assert) {
let result = await app.execute('ember test --path custom-base/sub-dir');
assert.equal(result.exitCode, 0, result.output);
});
});
});
}
2 changes: 1 addition & 1 deletion tests/ts-app-template/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
{{content-for "head"}}

<link integrity="" rel="stylesheet" href="/@embroider/virtual/vendor.css">
<link integrity="" rel="stylesheet" href="/assets/ts-app-template.css">
<link integrity="" rel="stylesheet" href="/@embroider/virtual/app.css">

{{content-for "head-footer"}}
</head>
Expand Down
2 changes: 1 addition & 1 deletion tests/ts-app-template/tests/index.html
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
{{content-for "test-head"}}

<link rel="stylesheet" href="/@embroider/virtual/vendor.css">
<link rel="stylesheet" href="/assets/ts-app-template.css">
<link rel="stylesheet" href="/@embroider/virtual/app.css">
<link rel="stylesheet" href="/@embroider/virtual/test-support.css">

{{content-for "head-footer"}}
Expand Down
Loading