Skip to content

Commit

Permalink
Merge branch 'main' into preview-with-vite
Browse files Browse the repository at this point in the history
  • Loading branch information
bluwy authored Jan 9, 2023
2 parents c0ab31b + f354114 commit 0ce0cb1
Show file tree
Hide file tree
Showing 43 changed files with 405 additions and 149 deletions.
6 changes: 4 additions & 2 deletions .changeset/lovely-worms-invite.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,10 @@
---
'@astrojs/deno': major
'@astrojs/netlify': major
'@astrojs/image': minor
'@astrojs/image': major
'astro': major
---

Builds chunks into the `assets` folder. This simplifies configuring immutable caching with your adapter provider as all files are now in the same `assets` folder.
**Breaking Change**: client assets are built to an `_astro` directory rather than the previous `assets` directory. This setting can now be controlled by the new `build` configuration option named `assets`.

This should simplify configuring immutable caching with your adapter provider as all files are now in the same `_astro` directory.
16 changes: 16 additions & 0 deletions .changeset/thin-seahorses-worry.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
---
'@astrojs/cloudflare': major
'@astrojs/deno': major
'@astrojs/netlify': major
'@astrojs/node': major
'@astrojs/svelte': major
'@astrojs/tailwind': major
'@astrojs/vercel': major
'@astrojs/vue': major
'@astrojs/markdown-remark': major
'@astrojs/image': minor
---

Make astro a peerDependency of integrations

This marks `astro` as a peerDependency of several packages that are already getting `major` version bumps. This is so we can more properly track the dependency between them and what version of Astro they are being used with.
19 changes: 19 additions & 0 deletions packages/astro/src/@types/astro.ts
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,25 @@ export interface AstroUserConfig {
* ```
*/
server?: string;
/**
* @docs
* @name build.assets
* @type {string}
* @default `'_astro'`
* @see outDir
* @version 2.0.0
* @description
* Specifies the directory in the build output where Astro-generated assets (bundled JS and CSS for example) should live.
*
* ```js
* {
* build: {
* assets: '_custom'
* }
* }
* ```
*/
assets?: string;
/**
* @docs
* @name build.serverEntry
Expand Down
77 changes: 22 additions & 55 deletions packages/astro/src/core/build/static-build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import * as eslexer from 'es-module-lexer';
import glob from 'fast-glob';
import fs from 'fs';
import { bgGreen, bgMagenta, black, dim } from 'kleur/colors';
import path from 'path';
import { fileURLToPath } from 'url';
import * as vite from 'vite';
import { astroBundleDelayedAssetPlugin } from '../../content/index.js';
Expand All @@ -11,8 +10,8 @@ import {
createBuildInternals,
eachPrerenderedPageData,
} from '../../core/build/internal.js';
import { emptyDir, removeDir } from '../../core/fs/index.js';
import { prependForwardSlash } from '../../core/path.js';
import { emptyDir, removeEmptyDirs } from '../../core/fs/index.js';
import { appendForwardSlash, prependForwardSlash } from '../../core/path.js';
import { isModeServerWithNoAdapter } from '../../core/util.js';
import { runHookBuildSetup } from '../../integrations/index.js';
import { PAGE_SCRIPT_ID } from '../../vite-plugin-scripts/index.js';
Expand Down Expand Up @@ -133,8 +132,10 @@ async function ssrBuild(opts: StaticBuildOptions, internals: BuildInternals, inp
input: [],
output: {
format: 'esm',
chunkFileNames: 'chunks/[name].[hash].mjs',
assetFileNames: 'assets/[name].[hash][extname]',
// Server chunks can't go in the assets (_astro) folder
// We need to keep these separate
chunkFileNames: `chunks/[name].[hash].mjs`,
assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
...viteConfig.build?.rollupOptions?.output,
entryFileNames: opts.buildConfig.serverEntry,
},
Expand Down Expand Up @@ -212,9 +213,9 @@ async function clientBuild(
input: Array.from(input),
output: {
format: 'esm',
entryFileNames: 'assets/[name].[hash].js',
chunkFileNames: 'assets/chunks/[name].[hash].js',
assetFileNames: 'assets/[name].[hash][extname]',
entryFileNames: `${settings.config.build.assets}/[name].[hash].js`,
chunkFileNames: `${settings.config.build.assets}/[name].[hash].js`,
assetFileNames: `${settings.config.build.assets}/[name].[hash][extname]`,
...viteConfig.build?.rollupOptions?.output,
},
preserveEntrySignatures: 'exports-only',
Expand Down Expand Up @@ -285,25 +286,8 @@ async function cleanStaticOutput(opts: StaticBuildOptions, internals: BuildInter
await fs.promises.writeFile(url, value, { encoding: 'utf8' });
})
);
// Map directories heads from the .mjs files
const directories: Set<string> = new Set();
files.forEach((i) => {
const splitFilePath = i.split(path.sep);
// If the path is more than just a .mjs filename itself
if (splitFilePath.length > 1) {
directories.add(splitFilePath[0]);
}
});
// Attempt to remove only those folders which are empty
await Promise.all(
Array.from(directories).map(async (filename) => {
const url = new URL(filename, out);
const folder = await fs.promises.readdir(url);
if (!folder.length) {
await fs.promises.rm(url, { recursive: true, force: true });
}
})
);

removeEmptyDirs(out);
}
}

Expand All @@ -321,28 +305,10 @@ async function cleanServerOutput(opts: StaticBuildOptions) {
await fs.promises.rm(url);
})
);
// Map directories heads from the .mjs files
const directories: Set<string> = new Set();
files.forEach((i) => {
const splitFilePath = i.split(path.sep);
// If the path is more than just a .mjs filename itself
if (splitFilePath.length > 1) {
directories.add(splitFilePath[0]);
}
});
// Attempt to remove only those folders which are empty
await Promise.all(
Array.from(directories).map(async (filename) => {
const url = new URL(filename, out);
const dir = await glob(fileURLToPath(url));
// Do not delete chunks/ directory!
if (filename === 'chunks') return;
if (!dir.length) {
await fs.promises.rm(url, { recursive: true, force: true });
}
})
);

removeEmptyDirs(out);
}

// Clean out directly if the outDir is outside of root
if (out.toString() !== opts.settings.config.outDir.toString()) {
// Copy assets before cleaning directory if outside root
Expand Down Expand Up @@ -374,22 +340,23 @@ async function ssrMoveAssets(opts: StaticBuildOptions) {
const serverRoot =
opts.settings.config.output === 'static' ? opts.buildConfig.client : opts.buildConfig.server;
const clientRoot = opts.buildConfig.client;
const serverAssets = new URL('./assets/', serverRoot);
const clientAssets = new URL('./assets/', clientRoot);
const files = await glob('assets/**/*', {
cwd: fileURLToPath(serverRoot),
const assets = opts.settings.config.build.assets;
const serverAssets = new URL(`./${assets}/`, appendForwardSlash(serverRoot.toString()));
const clientAssets = new URL(`./${assets}/`, appendForwardSlash(clientRoot.toString()));
const files = await glob(`**/*`, {
cwd: fileURLToPath(serverAssets),
});

if (files.length > 0) {
// Make the directory
await fs.promises.mkdir(clientAssets, { recursive: true });
await Promise.all(
files.map(async (filename) => {
const currentUrl = new URL(filename, serverRoot);
const clientUrl = new URL(filename, clientRoot);
const currentUrl = new URL(filename, appendForwardSlash(serverAssets.toString()));
const clientUrl = new URL(filename, appendForwardSlash(clientAssets.toString()));
return fs.promises.rename(currentUrl, clientUrl);
})
);
removeDir(serverAssets);
removeEmptyDirs(serverAssets);
}
}
3 changes: 3 additions & 0 deletions packages/astro/src/core/config/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ const ASTRO_CONFIG_DEFAULTS: AstroUserConfig & any = {
format: 'directory',
client: './dist/client/',
server: './dist/server/',
assets: '_astro',
serverEntry: 'entry.mjs',
},
server: {
Expand Down Expand Up @@ -102,6 +103,7 @@ export const AstroConfigSchema = z.object({
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.server)
.transform((val) => new URL(val)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
})
.optional()
Expand Down Expand Up @@ -246,6 +248,7 @@ export function createRelativeSchema(cmd: string, fileProtocolRoot: URL) {
.optional()
.default(ASTRO_CONFIG_DEFAULTS.build.server)
.transform((val) => new URL(val, fileProtocolRoot)),
assets: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.assets),
serverEntry: z.string().optional().default(ASTRO_CONFIG_DEFAULTS.build.serverEntry),
})
.optional()
Expand Down
19 changes: 19 additions & 0 deletions packages/astro/src/core/fs/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import fs from 'fs';
import path from 'path';
import { fileURLToPath } from 'url';
import { appendForwardSlash } from '../path.js';

const isWindows = process.platform === 'win32';

Expand All @@ -10,6 +11,24 @@ export function removeDir(_dir: URL): void {
fs.rmSync(dir, { recursive: true, force: true, maxRetries: 3 });
}

export function removeEmptyDirs(root: URL): void {
const dir = fileURLToPath(root);
if (!fs.statSync(dir).isDirectory()) return;
let files = fs.readdirSync(dir);

if (files.length > 0) {
files.map((file) => {
const url = new URL(`./${file}`, appendForwardSlash(root.toString()));
removeEmptyDirs(url);
});
files = fs.readdirSync(dir);
}

if (files.length === 0) {
fs.rmdirSync(dir);
}
}

export function emptyDir(_dir: URL, skip?: Set<string>): void {
const dir = fileURLToPath(_dir);
if (!fs.existsSync(dir)) return undefined;
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/test/0-css.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ describe('CSS', function () {
// get bundled CSS (will be hashed, hence DOM query)
html = await fixture.readFile('/index.html');
$ = cheerio.load(html);
const bundledCSSHREF = $('link[rel=stylesheet][href^=/assets/]').attr('href');
const bundledCSSHREF = $('link[rel=stylesheet][href^=/_astro/]').attr('href');
bundledCSS = (await fixture.readFile(bundledCSSHREF.replace(/^\/?/, '/')))
.replace(/\s/g, '')
.replace('/n', '');
Expand Down Expand Up @@ -364,7 +364,7 @@ describe('CSS', function () {
});

it('remove unused styles from client:load components', async () => {
const bundledAssets = await fixture.readdir('./assets');
const bundledAssets = await fixture.readdir('./_astro');
// SvelteDynamic styles is already included in the main page css asset
const unusedCssAsset = bundledAssets.find((asset) => /SvelteDynamic\..*\.css/.test(asset));
expect(unusedCssAsset, 'Found unused style ' + unusedCssAsset).to.be.undefined;
Expand Down
10 changes: 5 additions & 5 deletions packages/astro/test/astro-css-bundling.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { loadFixture } from './test-utils.js';
// note: the hashes should be deterministic, but updating the file contents will change hashes
// be careful not to test that the HTML simply contains CSS, because it always will! filename and quanity matter here (bundling).
const EXPECTED_CSS = {
'/index.html': ['/assets/'], // don’t match hashes, which change based on content
'/one/index.html': ['/assets/'],
'/two/index.html': ['/assets/'],
'/index.html': ['/_astro/'], // don’t match hashes, which change based on content
'/one/index.html': ['/_astro/'],
'/two/index.html': ['/_astro/'],
};
const UNEXPECTED_CSS = [
'/src/components/nav.css',
Expand Down Expand Up @@ -61,12 +61,12 @@ describe('CSS Bundling', function () {
});

it('there are 4 css files', async () => {
const dir = await fixture.readdir('/assets');
const dir = await fixture.readdir('/_astro');
expect(dir).to.have.a.lengthOf(4);
});

it('CSS includes hashes', async () => {
const [firstFound] = await fixture.readdir('/assets');
const [firstFound] = await fixture.readdir('/_astro');
expect(firstFound).to.match(/[a-z]+\.[0-9a-z]{8}\.css/);
});
});
Expand Down
2 changes: 1 addition & 1 deletion packages/astro/test/astro-dynamic.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -75,6 +75,6 @@ describe('Dynamic components subpath', () => {
expect($('astro-island').html()).to.equal('');
// test 2: has component url
const attr = $('astro-island').attr('component-url');
expect(attr).to.include(`blog/assets/PersistentCounter`);
expect(attr).to.include(`blog/_astro/PersistentCounter`);
});
});
4 changes: 2 additions & 2 deletions packages/astro/test/astro-envs.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ describe('Environment Variables', () => {
});

it('includes public env in client-side JS', async () => {
let dirs = await fixture.readdir('/assets');
let dirs = await fixture.readdir('/_astro');
console.log(dirs);
let found = false;

Expand All @@ -62,7 +62,7 @@ describe('Environment Variables', () => {
await Promise.all(
dirs.map(async (path) => {
if (path.endsWith('.js')) {
let js = await fixture.readFile(`/assets/${path}`);
let js = await fixture.readFile(`/_astro/${path}`);
if (js.includes('BLUE_BAYOU')) {
found = true;
}
Expand Down
4 changes: 2 additions & 2 deletions packages/astro/test/astro-scripts.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ describe('Scripts (hoisted and not)', () => {

// test 2: inside assets
let entryURL = $('script').attr('src');
expect(entryURL.includes('assets/')).to.equal(true);
expect(entryURL.includes('_astro/')).to.equal(true);
});

it('External page using non-hoist scripts that are not modules are built standalone', async () => {
Expand All @@ -85,7 +85,7 @@ describe('Scripts (hoisted and not)', () => {

// test 2: inside assets
let entryURL = $('script').attr('src');
expect(entryURL.includes('assets/')).to.equal(true);
expect(entryURL.includes('_astro/')).to.equal(true);
});

it('Scripts added via Astro.glob are hoisted', async () => {
Expand Down
Loading

0 comments on commit 0ce0cb1

Please sign in to comment.