Skip to content

Commit

Permalink
Merge branch 'main' into feature/standalonemiddleware
Browse files Browse the repository at this point in the history
  • Loading branch information
nm123github authored Jul 22, 2024
2 parents 2e2829c + 8068131 commit 28c42ca
Show file tree
Hide file tree
Showing 9 changed files with 103 additions and 12 deletions.
5 changes: 5 additions & 0 deletions .changeset/clever-pillows-boil.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Excludes hoisted scripts and styles from Astro components imported with `?url` or `?raw`
5 changes: 5 additions & 0 deletions .changeset/new-melons-cross.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'astro': patch
---

Fixes a case where the build was failing when `experimental.actions` was enabled, an adapter was in use, and there were not actions inside the user code base.
3 changes: 3 additions & 0 deletions packages/astro/src/actions/consts.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,6 @@
export const VIRTUAL_MODULE_ID = 'astro:actions';
export const RESOLVED_VIRTUAL_MODULE_ID = '\0' + VIRTUAL_MODULE_ID;
export const ACTIONS_TYPES_FILE = 'actions.d.ts';
export const VIRTUAL_INTERNAL_MODULE_ID = 'astro:internal-actions';
export const RESOLVED_VIRTUAL_INTERNAL_MODULE_ID = '\0astro:internal-actions';
export const NOOP_ACTIONS = '\0noop-actions';
60 changes: 53 additions & 7 deletions packages/astro/src/actions/index.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,25 @@
import fsMod from 'node:fs';
import type { Plugin as VitePlugin } from 'vite';
import type { AstroIntegration } from '../@types/astro.js';
import type { AstroIntegration, AstroSettings } from '../@types/astro.js';
import { ActionsWithoutServerOutputError } from '../core/errors/errors-data.js';
import { AstroError } from '../core/errors/errors.js';
import { isServerLikeOutput, viteID } from '../core/util.js';
import { ACTIONS_TYPES_FILE, RESOLVED_VIRTUAL_MODULE_ID, VIRTUAL_MODULE_ID } from './consts.js';
import {
ACTIONS_TYPES_FILE,
NOOP_ACTIONS,
RESOLVED_VIRTUAL_INTERNAL_MODULE_ID,
RESOLVED_VIRTUAL_MODULE_ID,
VIRTUAL_INTERNAL_MODULE_ID,
VIRTUAL_MODULE_ID,
} from './consts.js';

export default function astroActions({ fs = fsMod }: { fs?: typeof fsMod }): AstroIntegration {
export default function astroActions({
fs = fsMod,
settings,
}: {
fs?: typeof fsMod;
settings: AstroSettings;
}): AstroIntegration {
return {
name: VIRTUAL_MODULE_ID,
hooks: {
Expand All @@ -22,10 +35,7 @@ export default function astroActions({ fs = fsMod }: { fs?: typeof fsMod }): Ast
);
params.updateConfig({
vite: {
define: {
'import.meta.env.ACTIONS_PATH': stringifiedActionsImport,
},
plugins: [vitePluginActions(fs)],
plugins: [vitePluginUserActions({ settings }), vitePluginActions(fs)],
},
});

Expand All @@ -50,6 +60,42 @@ export default function astroActions({ fs = fsMod }: { fs?: typeof fsMod }): Ast
};
}

/**
* This plugin is responsible to load the known file `actions/index.js` / `actions.js`
* If the file doesn't exist, it returns an empty object.
* @param settings
*/
export function vitePluginUserActions({ settings }: { settings: AstroSettings }): VitePlugin {
let resolvedActionsId: string;
return {
name: '@astro/plugin-actions',
async resolveId(id) {
if (id === NOOP_ACTIONS) {
return NOOP_ACTIONS;
}
if (id === VIRTUAL_INTERNAL_MODULE_ID) {
const resolvedModule = await this.resolve(
`${decodeURI(new URL('actions', settings.config.srcDir).pathname)}`
);

if (!resolvedModule) {
return NOOP_ACTIONS;
}
resolvedActionsId = resolvedModule.id;
return RESOLVED_VIRTUAL_INTERNAL_MODULE_ID;
}
},

load(id) {
if (id === NOOP_ACTIONS) {
return 'export const server = {}';
} else if (id === RESOLVED_VIRTUAL_INTERNAL_MODULE_ID) {
return `export { server } from '${resolvedActionsId}';`;
}
},
};
}

const vitePluginActions = (fs: typeof fsMod): VitePlugin => ({
name: VIRTUAL_MODULE_ID,
enforce: 'pre',
Expand Down
6 changes: 4 additions & 2 deletions packages/astro/src/actions/runtime/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,14 +12,16 @@ export type MaybePromise<T> = T | Promise<T>;

/**
* Get server-side action based on the route path.
* Imports from `import.meta.env.ACTIONS_PATH`, which maps to
* Imports from the virtual module `astro:internal-actions`, which maps to
* the user's `src/actions/index.ts` file at build-time.
*/
export async function getAction(
path: string
): Promise<((param: unknown) => MaybePromise<unknown>) | undefined> {
const pathKeys = path.replace('/_actions/', '').split('.');
let { server: actionLookup } = await import(import.meta.env.ACTIONS_PATH);
// @ts-expect-error virtual module
let { server: actionLookup } = await import('astro:internal-actions');

for (const key of pathKeys) {
if (!(key in actionLookup)) {
return undefined;
Expand Down
4 changes: 3 additions & 1 deletion packages/astro/src/integrations/hooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,7 @@ export async function runHookConfigSetup({
}
if (settings.config.experimental?.actions) {
const { default: actionsIntegration } = await import('../actions/index.js');
settings.config.integrations.push(actionsIntegration({ fs }));
settings.config.integrations.push(actionsIntegration({ fs, settings }));
}

let updatedConfig: AstroConfig = { ...settings.config };
Expand Down Expand Up @@ -230,9 +230,11 @@ export async function runHookConfigSetup({
const exts = (input.flat(Infinity) as string[]).map((ext) => `.${ext.replace(/^\./, '')}`);
updatedSettings.pageExtensions.push(...exts);
}

function addContentEntryType(contentEntryType: ContentEntryType) {
updatedSettings.contentEntryTypes.push(contentEntryType);
}

function addDataEntryType(dataEntryType: DataEntryType) {
updatedSettings.dataEntryTypes.push(dataEntryType);
}
Expand Down
12 changes: 12 additions & 0 deletions packages/astro/src/vite-plugin-astro-server/vite.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import npath from 'node:path';
import { SUPPORTED_MARKDOWN_FILE_EXTENSIONS } from '../core/constants.js';
import type { ModuleLoader, ModuleNode } from '../core/module-loader/index.js';
import { unwrapId } from '../core/util.js';
import { hasSpecialQueries } from '../vite-plugin-utils/index.js';
import { isCSSRequest } from './util.js';

/**
Expand Down Expand Up @@ -42,6 +43,10 @@ export async function* crawlGraph(
if (id === entry.id) {
scanned.add(id);

// NOTE: It may be worth revisiting if we can crawl direct imports of the module since
// `.importedModules` would also include modules that are dynamically watched, not imported.
// That way we no longer need the below `continue` skips.

// CSS requests `importedModules` are usually from `@import`, but we don't really need
// to crawl into those as the `@import` code are already inlined into this `id`.
// If CSS requests `importedModules` contain non-CSS files, e.g. Tailwind might add HMR
Expand All @@ -50,6 +55,13 @@ export async function* crawlGraph(
if (isCSSRequest(id)) {
continue;
}
// Some special Vite queries like `?url` or `?raw` are known to be a simple default export
// and doesn't have any imports to crawl. However, since they would `this.addWatchFile` the
// underlying module, our logic would crawl into them anyways which is incorrect as they
// don't take part in the final rendering, so we skip it here.
if (hasSpecialQueries(id)) {
continue;
}

for (const importedModule of entry.importedModules) {
if (!importedModule.id) continue;
Expand Down
18 changes: 16 additions & 2 deletions packages/astro/test/astro-basic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -162,7 +162,14 @@ describe('Astro basic build', () => {
it('Handles importing .astro?raw correctly', async () => {
const html = await fixture.readFile('/import-queries/raw/index.html');
const $ = cheerio.load(html);
assert.equal($('.raw-value').text(), '<h1>Hello</h1>\n');
const rawValue = $('.raw-value').text();
assert.match(rawValue, /<h1>Hello<\/h1>/);
assert.match(rawValue, /<script>/);
assert.match(rawValue, /<style>/);
// The rest of HTML should not contain any scripts or styles hoisted from the raw import
const otherHtml = html.replace(rawValue, '');
assert.doesNotMatch(otherHtml, /<script/);
assert.doesNotMatch(otherHtml, /<style/);
});

describe('preview', () => {
Expand Down Expand Up @@ -223,6 +230,13 @@ describe('Astro basic development', () => {
assert.equal(res.status, 200);
const html = await res.text();
const $ = cheerio.load(html);
assert.equal($('.raw-value').text(), '<h1>Hello</h1>\n');
const rawValue = $('.raw-value').text();
assert.match(rawValue, /<h1>Hello<\/h1>/);
assert.match(rawValue, /<script>/);
assert.match(rawValue, /<style>/);
// The rest of HTML should not contain any scripts or styles hoisted from the raw import.
// However we don't check them here as dev plugins could add scripts and styles dynam
assert.doesNotMatch(html, /_content.astro\?astro&type=style/);
assert.doesNotMatch(html, /_content.astro\?astro&type=script/);
});
});
Original file line number Diff line number Diff line change
@@ -1 +1,3 @@
<h1>Hello</h1>
<script>console.log('Should not log')</script>
<style>h1 { color: red; }</style>

0 comments on commit 28c42ca

Please sign in to comment.