mirror of
https://github.com/withastro/astro.git
synced 2024-12-30 22:03:56 -05:00
Add Windows Support (#93)
* Add Windows to test suite * Try implicit URL
This commit is contained in:
parent
ec75312a15
commit
034674c88c
12 changed files with 47 additions and 28 deletions
6
.github/workflows/nodejs.yml
vendored
6
.github/workflows/nodejs.yml
vendored
|
@ -4,11 +4,11 @@ on: [push]
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
test:
|
test:
|
||||||
runs-on: ubuntu-latest
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
strategy:
|
strategy:
|
||||||
matrix:
|
matrix:
|
||||||
node-version: [14.x]
|
os: [ubuntu-latest, windows-latest]
|
||||||
|
node-version: [14.x, 15.x]
|
||||||
|
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v1
|
- uses: actions/checkout@v1
|
||||||
|
|
|
@ -4,6 +4,7 @@ import type { LoadResult } from './runtime';
|
||||||
|
|
||||||
import { existsSync, promises as fsPromises } from 'fs';
|
import { existsSync, promises as fsPromises } from 'fs';
|
||||||
import { relative as pathRelative } from 'path';
|
import { relative as pathRelative } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { fdir } from 'fdir';
|
import { fdir } from 'fdir';
|
||||||
import { defaultLogDestination, error } from './logger.js';
|
import { defaultLogDestination, error } from './logger.js';
|
||||||
import { createRuntime } from './runtime.js';
|
import { createRuntime } from './runtime.js';
|
||||||
|
@ -22,7 +23,7 @@ async function allPages(root: URL) {
|
||||||
const api = new fdir()
|
const api = new fdir()
|
||||||
.filter((p) => /\.(astro|md)$/.test(p))
|
.filter((p) => /\.(astro|md)$/.test(p))
|
||||||
.withFullPaths()
|
.withFullPaths()
|
||||||
.crawl(root.pathname);
|
.crawl(fileURLToPath(root));
|
||||||
const files = await api.withPromise();
|
const files = await api.withPromise();
|
||||||
return files as string[];
|
return files as string[];
|
||||||
}
|
}
|
||||||
|
@ -47,7 +48,7 @@ async function writeResult(result: LoadResult, outPath: URL, encoding: null | 'u
|
||||||
if (result.statusCode === 500 || result.statusCode === 404) {
|
if (result.statusCode === 500 || result.statusCode === 404) {
|
||||||
error(logging, 'build', result.error || result.statusCode);
|
error(logging, 'build', result.error || result.statusCode);
|
||||||
} else if (result.statusCode !== 200) {
|
} else if (result.statusCode !== 200) {
|
||||||
error(logging, 'build', `Unexpected load result (${result.statusCode}) for ${outPath.pathname}`);
|
error(logging, 'build', `Unexpected load result (${result.statusCode}) for ${fileURLToPath(outPath)}`);
|
||||||
} else {
|
} else {
|
||||||
const bytes = result.contents;
|
const bytes = result.contents;
|
||||||
await writeFilep(outPath, bytes, encoding);
|
await writeFilep(outPath, bytes, encoding);
|
||||||
|
@ -119,7 +120,7 @@ export async function build(astroConfig: AstroConfig): Promise<0 | 1> {
|
||||||
|
|
||||||
if (existsSync(astroConfig.public)) {
|
if (existsSync(astroConfig.public)) {
|
||||||
const pub = astroConfig.public;
|
const pub = astroConfig.public;
|
||||||
const publicFiles = (await new fdir().withFullPaths().crawl(pub.pathname).withPromise()) as string[];
|
const publicFiles = (await new fdir().withFullPaths().crawl(fileURLToPath(pub)).withPromise()) as string[];
|
||||||
for (const filepath of publicFiles) {
|
for (const filepath of publicFiles) {
|
||||||
const fileUrl = new URL(`file://${filepath}`);
|
const fileUrl = new URL(`file://${filepath}`);
|
||||||
const rel = pathRelative(pub.pathname, fileUrl.pathname);
|
const rel = pathRelative(pub.pathname, fileUrl.pathname);
|
||||||
|
|
|
@ -6,6 +6,7 @@ import type { LogOptions } from '../logger';
|
||||||
|
|
||||||
import esbuild from 'esbuild';
|
import esbuild from 'esbuild';
|
||||||
import { promises as fsPromises } from 'fs';
|
import { promises as fsPromises } from 'fs';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { parse } from '../parser/index.js';
|
import { parse } from '../parser/index.js';
|
||||||
import { transform } from '../compiler/transform/index.js';
|
import { transform } from '../compiler/transform/index.js';
|
||||||
import { convertMdToAstroSource } from '../compiler/index.js';
|
import { convertMdToAstroSource } from '../compiler/index.js';
|
||||||
|
@ -93,7 +94,7 @@ export async function collectDynamicImports(filename: URL, { astroConfig, loggin
|
||||||
}
|
}
|
||||||
|
|
||||||
await transform(ast, {
|
await transform(ast, {
|
||||||
filename: filename.pathname,
|
filename: fileURLToPath(filename),
|
||||||
fileID: '',
|
fileID: '',
|
||||||
compileOptions: {
|
compileOptions: {
|
||||||
astroConfig,
|
astroConfig,
|
||||||
|
@ -281,7 +282,7 @@ export async function bundle(imports: Set<string>, { runtime, dist }: BundleOpti
|
||||||
const build = await rollup(inputOptions);
|
const build = await rollup(inputOptions);
|
||||||
|
|
||||||
const outputOptions: OutputOptions = {
|
const outputOptions: OutputOptions = {
|
||||||
dir: dist.pathname,
|
dir: fileURLToPath(dist),
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
exports: 'named',
|
exports: 'named',
|
||||||
entryFileNames(chunk) {
|
entryFileNames(chunk) {
|
||||||
|
|
|
@ -2,6 +2,7 @@ import crypto from 'crypto';
|
||||||
import fs from 'fs';
|
import fs from 'fs';
|
||||||
import { createRequire } from 'module';
|
import { createRequire } from 'module';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import autoprefixer from 'autoprefixer';
|
import autoprefixer from 'autoprefixer';
|
||||||
import postcss, { Plugin } from 'postcss';
|
import postcss, { Plugin } from 'postcss';
|
||||||
import postcssKeyframes from 'postcss-icss-keyframes';
|
import postcssKeyframes from 'postcss-icss-keyframes';
|
||||||
|
@ -165,7 +166,7 @@ export default function transformStyles({ compileOptions, filename, fileID }: Tr
|
||||||
if (miniCache.tailwindEnabled === undefined) {
|
if (miniCache.tailwindEnabled === undefined) {
|
||||||
const tailwindNames = ['tailwind.config.js', 'tailwind.config.mjs'];
|
const tailwindNames = ['tailwind.config.js', 'tailwind.config.mjs'];
|
||||||
for (const loc of tailwindNames) {
|
for (const loc of tailwindNames) {
|
||||||
const tailwindLoc = path.join(compileOptions.astroConfig.projectRoot.pathname, loc);
|
const tailwindLoc = path.join(fileURLToPath(compileOptions.astroConfig.projectRoot), loc);
|
||||||
if (fs.existsSync(tailwindLoc)) {
|
if (fs.existsSync(tailwindLoc)) {
|
||||||
miniCache.tailwindEnabled = true; // Success! We have a Tailwind config file.
|
miniCache.tailwindEnabled = true; // Success! We have a Tailwind config file.
|
||||||
debug(compileOptions.logging, 'tailwind', 'Found config. Enabling.');
|
debug(compileOptions.logging, 'tailwind', 'Found config. Enabling.');
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import type { SnowpackDevServer, ServerRuntime as SnowpackServerRuntime, SnowpackConfig } from 'snowpack';
|
import type { SnowpackDevServer, ServerRuntime as SnowpackServerRuntime, SnowpackConfig } from 'snowpack';
|
||||||
import type { AstroConfig, CollectionResult, CreateCollection, Params, RuntimeMode } from './@types/astro';
|
import type { AstroConfig, CollectionResult, CreateCollection, Params, RuntimeMode } from './@types/astro';
|
||||||
import type { LogOptions } from './logger';
|
import type { LogOptions } from './logger';
|
||||||
|
@ -223,19 +224,19 @@ async function createSnowpack(astroConfig: AstroConfig, env: Record<string, any>
|
||||||
};
|
};
|
||||||
|
|
||||||
const mountOptions = {
|
const mountOptions = {
|
||||||
[astroRoot.pathname]: '/_astro',
|
[fileURLToPath(astroRoot)]: '/_astro',
|
||||||
[internalPath.pathname]: '/_astro_internal',
|
[fileURLToPath(internalPath)]: '/_astro_internal',
|
||||||
};
|
};
|
||||||
|
|
||||||
if (existsSync(astroConfig.public)) {
|
if (existsSync(astroConfig.public)) {
|
||||||
mountOptions[astroConfig.public.pathname] = '/';
|
mountOptions[fileURLToPath(astroConfig.public)] = '/';
|
||||||
}
|
}
|
||||||
|
|
||||||
const snowpackConfig = await loadConfiguration({
|
const snowpackConfig = await loadConfiguration({
|
||||||
root: projectRoot.pathname,
|
root: fileURLToPath(projectRoot),
|
||||||
mount: mountOptions,
|
mount: mountOptions,
|
||||||
plugins: [
|
plugins: [
|
||||||
[new URL('../snowpack-plugin.cjs', import.meta.url).pathname, astroPlugOptions],
|
[fileURLToPath(new URL('../snowpack-plugin.cjs', import.meta.url)), astroPlugOptions],
|
||||||
require.resolve('@snowpack/plugin-sass'),
|
require.resolve('@snowpack/plugin-sass'),
|
||||||
require.resolve('@snowpack/plugin-svelte'),
|
require.resolve('@snowpack/plugin-svelte'),
|
||||||
require.resolve('@snowpack/plugin-vue'),
|
require.resolve('@snowpack/plugin-vue'),
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { existsSync } from 'fs';
|
import { existsSync } from 'fs';
|
||||||
import path from 'path';
|
import path from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { fdir, PathsOutput } from 'fdir';
|
import { fdir, PathsOutput } from 'fdir';
|
||||||
|
|
||||||
interface PageLocation {
|
interface PageLocation {
|
||||||
|
@ -99,9 +100,10 @@ const crawler = new fdir();
|
||||||
|
|
||||||
/** load a collection route */
|
/** load a collection route */
|
||||||
function loadCollection(url: string, astroRoot: URL): { currentPage?: number; location: PageLocation } | undefined {
|
function loadCollection(url: string, astroRoot: URL): { currentPage?: number; location: PageLocation } | undefined {
|
||||||
const pages = (crawler.glob('**/*').crawl(path.join(astroRoot.pathname, 'pages')).sync() as PathsOutput).filter(
|
const pages = (crawler
|
||||||
(filepath) => filepath.startsWith('$') || filepath.includes('/$')
|
.glob('**/*')
|
||||||
);
|
.crawl(path.join(fileURLToPath(astroRoot), 'pages'))
|
||||||
|
.sync() as PathsOutput).filter((filepath) => filepath.startsWith('$') || filepath.includes('/$'));
|
||||||
for (const pageURL of pages) {
|
for (const pageURL of pages) {
|
||||||
const reqURL = new RegExp('^/' + pageURL.replace(/\$([^/]+)\.astro/, '$1') + '/?(.*)');
|
const reqURL = new RegExp('^/' + pageURL.replace(/\$([^/]+)\.astro/, '$1') + '/?(.*)');
|
||||||
const match = url.match(reqURL);
|
const match = url.match(reqURL);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { suite } from 'uvu';
|
import { suite } from 'uvu';
|
||||||
import * as assert from 'uvu/assert';
|
import * as assert from 'uvu/assert';
|
||||||
import { loadConfig } from '../lib/config.js';
|
import { loadConfig } from '../lib/config.js';
|
||||||
|
@ -9,7 +10,7 @@ let runtime, setupError;
|
||||||
|
|
||||||
DType.before(async () => {
|
DType.before(async () => {
|
||||||
try {
|
try {
|
||||||
const astroConfig = await loadConfig(new URL('./fixtures/astro-doctype', import.meta.url).pathname);
|
const astroConfig = await loadConfig(fileURLToPath(new URL('./fixtures/astro-doctype', import.meta.url)));
|
||||||
|
|
||||||
const logging = {
|
const logging = {
|
||||||
level: 'error',
|
level: 'error',
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
import { existsSync, promises as fsPromises } from 'fs';
|
import { existsSync, promises as fsPromises } from 'fs';
|
||||||
import { join } from 'path';
|
import { join } from 'path';
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { suite } from 'uvu';
|
import { suite } from 'uvu';
|
||||||
import * as assert from 'uvu/assert';
|
import * as assert from 'uvu/assert';
|
||||||
import { createRuntime } from '../lib/runtime.js';
|
import { createRuntime } from '../lib/runtime.js';
|
||||||
|
@ -14,7 +15,7 @@ const Markdown = suite('Astro Markdown');
|
||||||
let runtime, setupError, fixturePath, astroConfig;
|
let runtime, setupError, fixturePath, astroConfig;
|
||||||
|
|
||||||
Markdown.before(async () => {
|
Markdown.before(async () => {
|
||||||
fixturePath = new URL('./fixtures/astro-markdown', import.meta.url).pathname;
|
fixturePath = fileURLToPath(new URL('./fixtures/astro-markdown', import.meta.url));
|
||||||
|
|
||||||
astroConfig = await loadConfig(fixturePath);
|
astroConfig = await loadConfig(fixturePath);
|
||||||
|
|
||||||
|
|
|
@ -42,14 +42,20 @@ StylesSSR('Has correct CSS classes', async ({ runtime }) => {
|
||||||
const MUST_HAVE_CLASSES = {
|
const MUST_HAVE_CLASSES = {
|
||||||
'#react-css': 'react-title',
|
'#react-css': 'react-title',
|
||||||
'#vue-css': 'vue-title',
|
'#vue-css': 'vue-title',
|
||||||
'#vue-css-modules': '_title_1gi0u_2', // ⚠️ may be flaky
|
'#vue-css-modules': 'title', // ⚠️ this is the inverse
|
||||||
'#vue-scoped': 'vue-title', // also has data-v-* property
|
'#vue-scoped': 'vue-title', // also has data-v-* property
|
||||||
'#svelte-scoped': 'svelte-title', // also has additional class
|
'#svelte-scoped': 'svelte-title', // also has additional class
|
||||||
};
|
};
|
||||||
|
|
||||||
for (const [selector, className] of Object.entries(MUST_HAVE_CLASSES)) {
|
for (const [selector, className] of Object.entries(MUST_HAVE_CLASSES)) {
|
||||||
const el = $(selector);
|
const el = $(selector);
|
||||||
assert.ok(el.attr('class').includes(className));
|
if (selector === '#vue-css-modules') {
|
||||||
|
// this will generate differently on Unix vs Windows. Here we simply test that it has transformed
|
||||||
|
assert.not.equal(el.attr('class'), 'title');
|
||||||
|
} else {
|
||||||
|
// if this is not a CSS module, it should remain as expected
|
||||||
|
assert.ok(el.attr('class').includes(className));
|
||||||
|
}
|
||||||
|
|
||||||
// add’l test: Vue Scoped styles should have data-v-* attribute
|
// add’l test: Vue Scoped styles should have data-v-* attribute
|
||||||
if (selector === '#vue-scoped') {
|
if (selector === '#vue-scoped') {
|
||||||
|
@ -81,7 +87,7 @@ StylesSSR('CSS Module support in .astro', async ({ runtime }) => {
|
||||||
})
|
})
|
||||||
);
|
);
|
||||||
|
|
||||||
assert.equal(css, `.wrapper${scopedClass}{margin-left:auto;margin-right:auto;max-width:1200px}`);
|
assert.match(css, `.wrapper${scopedClass}{margin-left:auto;margin-right:auto;max-width:1200px}`);
|
||||||
|
|
||||||
// test 2: element received .astro-XXXXXX class (this selector will succeed if transformed correctly)
|
// test 2: element received .astro-XXXXXX class (this selector will succeed if transformed correctly)
|
||||||
const wrapper = $(`.wrapper${scopedClass}`);
|
const wrapper = $(`.wrapper${scopedClass}`);
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { createRuntime } from '../lib/runtime.js';
|
import { createRuntime } from '../lib/runtime.js';
|
||||||
import { loadConfig } from '../lib/config.js';
|
import { loadConfig } from '../lib/config.js';
|
||||||
import * as assert from 'uvu/assert';
|
import * as assert from 'uvu/assert';
|
||||||
|
@ -6,7 +7,7 @@ export function setup(Suite, fixturePath) {
|
||||||
let runtime, setupError;
|
let runtime, setupError;
|
||||||
|
|
||||||
Suite.before(async (context) => {
|
Suite.before(async (context) => {
|
||||||
const astroConfig = await loadConfig(new URL(fixturePath, import.meta.url).pathname);
|
const astroConfig = await loadConfig(fileURLToPath(new URL(fixturePath, import.meta.url)));
|
||||||
|
|
||||||
const logging = {
|
const logging = {
|
||||||
level: 'error',
|
level: 'error',
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { suite } from 'uvu';
|
import { suite } from 'uvu';
|
||||||
import * as assert from 'uvu/assert';
|
import * as assert from 'uvu/assert';
|
||||||
import { createRuntime } from '../lib/runtime.js';
|
import { createRuntime } from '../lib/runtime.js';
|
||||||
|
@ -9,7 +10,7 @@ const React = suite('React Components');
|
||||||
let runtime, setupError;
|
let runtime, setupError;
|
||||||
|
|
||||||
React.before(async () => {
|
React.before(async () => {
|
||||||
const astroConfig = await loadConfig(new URL('./fixtures/react-component', import.meta.url).pathname);
|
const astroConfig = await loadConfig(fileURLToPath(new URL('./fixtures/react-component', import.meta.url)));
|
||||||
|
|
||||||
const logging = {
|
const logging = {
|
||||||
level: 'error',
|
level: 'error',
|
||||||
|
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { fileURLToPath } from 'url';
|
||||||
import { suite } from 'uvu';
|
import { suite } from 'uvu';
|
||||||
import * as assert from 'uvu/assert';
|
import * as assert from 'uvu/assert';
|
||||||
import { createRuntime } from '../lib/runtime.js';
|
import { createRuntime } from '../lib/runtime.js';
|
||||||
|
@ -15,9 +16,9 @@ let runtime, cwd, setupError;
|
||||||
SnowpackDev.before(async () => {
|
SnowpackDev.before(async () => {
|
||||||
// Bug: Snowpack config is still loaded relative to the current working directory.
|
// Bug: Snowpack config is still loaded relative to the current working directory.
|
||||||
cwd = process.cwd();
|
cwd = process.cwd();
|
||||||
process.chdir(new URL('../examples/snowpack/', import.meta.url).pathname);
|
process.chdir(fileURLToPath(new URL('../examples/snowpack/', import.meta.url)));
|
||||||
|
|
||||||
const astroConfig = await loadConfig(new URL('../examples/snowpack', import.meta.url).pathname);
|
const astroConfig = await loadConfig(fileURLToPath(new URL('../examples/snowpack', import.meta.url)));
|
||||||
|
|
||||||
const logging = {
|
const logging = {
|
||||||
level: 'error',
|
level: 'error',
|
||||||
|
@ -53,9 +54,11 @@ async function* allPageFiles(root) {
|
||||||
/** create an iterator for all pages and yield the relative paths */
|
/** create an iterator for all pages and yield the relative paths */
|
||||||
async function* allPages(root) {
|
async function* allPages(root) {
|
||||||
for await (let fileURL of allPageFiles(root)) {
|
for await (let fileURL of allPageFiles(root)) {
|
||||||
let bare = fileURL.pathname.replace(/\.(astro|md)$/, '').replace(/index$/, '');
|
let bare = fileURLToPath(fileURL)
|
||||||
|
.replace(/\.(astro|md)$/, '')
|
||||||
|
.replace(/index$/, '');
|
||||||
|
|
||||||
yield '/' + pathRelative(root.pathname, bare);
|
yield '/' + pathRelative(fileURLToPath(root), bare);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue