fix npm ci

This commit is contained in:
Rene Haas
2022-10-14 14:39:31 +02:00
parent d81692b53b
commit b61bbe5f03
23 changed files with 130 additions and 7564 deletions
-1
View File
@@ -1,6 +1,5 @@
{
"private": true,
"name": "browser-testing",
"version": "0.0.0",
"main": "./dist/index.js",
"types": "./dist/index.d.ts",
-1
View File
@@ -1,6 +1,5 @@
{
"private": true,
"name": "@~local/config",
"exports": {
"./resolve": "./src/resolve.json",
"./playwright": "./src/playwright.js",
+6
View File
@@ -0,0 +1,6 @@
{
"private": true,
"sideEffects": false,
"main": "src/esbuild.js",
"type": "module"
}
+53
View File
@@ -0,0 +1,53 @@
import esbuildOriginal from 'esbuild';
import { writeOnlyChanges } from './writeOnlyChanges.js';
import { esbuildClearOldBuild } from './plugins/clearOldBuild.js';
import { esbuildPluginStyles } from './plugins/styles.js';
import { esbuildPluginTailwind } from './plugins/tailwind.js';
import { esbuildPluginExternal } from './plugins/external.js';
const changesMap = new Map();
const writeBuild = async (build) => {
if (build?.outputFiles) {
await writeOnlyChanges(build.outputFiles, changesMap);
}
};
export const esbuild = async (options = {}, { tailwindConfig = './tailwind.config.js' } = {}) => {
const buildOptions = {
bundle: true,
splitting: true,
allowOverwrite: true,
incremental: true,
metafile: true,
write: false,
format: 'esm',
platform: 'node',
chunkNames: 'chunks/[name]-[hash]',
assetNames: 'assets/[dir]/[name]',
entryNames: '[dir]/[name]',
jsx: 'automatic',
...options,
external: [...(options.external || [])],
watch: options.watch && {
async onRebuild(_, rebuildResult) {
await writeBuild(rebuildResult);
},
},
plugins: [
esbuildClearOldBuild(),
esbuildPluginStyles({
sassFilesRegex: /\.s[ac]ss$/,
cssFilesRegex: /\.css$/,
}),
esbuildPluginTailwind({
tailwindConfig,
tailwindCssFileRegex: /tailwind.*\.css$/,
}),
esbuildPluginExternal(),
...(options.plugins || []),
],
};
const build = await esbuildOriginal.build(buildOptions);
await writeBuild(build);
return build;
};
@@ -0,0 +1,4 @@
const isExtendedLengthPath = /^\\\\\?\\/;
export const normalizePathSlashes = (path) =>
isExtendedLengthPath.test(path) ? path : path.replace(/\\/g, '/');
@@ -0,0 +1,19 @@
import fs from 'node:fs';
import path from 'node:path';
export const esbuildClearOldBuild = () => {
let cleared = false;
return {
name: 'clearOldBuild',
setup(build) {
const initBuildOptions = build.initialOptions;
build.onStart(() => {
if (!cleared && initBuildOptions.outdir) {
fs.rmSync(path.resolve(initBuildOptions.outdir), { recursive: true, force: true });
cleared = true;
}
});
},
};
};
+25
View File
@@ -0,0 +1,25 @@
const externalRegex = /node_modules/;
export const esbuildPluginExternal = () => ({
name: 'external',
setup(build) {
build.onResolve({ filter: /.*/, namespace: 'file' }, async (args) => {
const { resolveDir, kind } = args;
if (kind !== 'entry-point') {
const { path: resolvedPath } = await build.resolve(args.path, {
resolveDir,
kind,
namespace: 'resolve-pls',
});
if (externalRegex.test(resolvedPath)) {
return {
path: args.path,
external: true,
};
}
}
});
},
});
+234
View File
@@ -0,0 +1,234 @@
import fs from 'node:fs';
import path from 'node:path';
import crypto from 'node:crypto';
import sass from 'sass';
import esbuild from 'esbuild';
import postcss from 'postcss';
import autoprefixer from 'autoprefixer';
import { normalizePathSlashes } from '../normalizePathSlashes.js';
const getHash = (content) => crypto.createHash('sha1').update(content).digest('hex');
export const esbuildPluginStyles = (options) => {
const {
cssBuildOptions = {},
cssModulesRegex = null,
sassFilesRegex = /\.s[ac]ss$/,
include = /\.(css|scss|sass)$/,
} = options;
const replaceExtensionRegex = /\.[^.]*$/;
const sassCache = new Map();
const postcssCache = new Map();
const esbuildCache = new Map();
const replaceExtension = (filePath, replacement = '') =>
filePath.replace(
replaceExtensionRegex,
typeof replacement === 'function'
? replacement((filePath.match(replaceExtensionRegex) || [])[0] || '')
: replacement
);
const transpileSass = (css, filePath) => {
const currHash = getHash(css);
const [cacheHash] = sassCache.get(filePath) || [];
if (currHash !== cacheHash) {
try {
const result = sass.compile(filePath);
sassCache.set(filePath, [currHash, result.css]);
} catch {}
}
return sassCache.get(filePath)[1] || css;
};
const transpilePostCss = async (source, filePath) => {
const currHash = getHash(source);
const [cacheHash] = postcssCache.get(filePath) || [];
if (currHash !== cacheHash) {
let cssModulesJson;
const plugins = [autoprefixer()].filter(Boolean);
try {
const { css } = await postcss(plugins).process(source, {
from: filePath,
});
const result = [css, cssModulesJson];
postcssCache.set(filePath, [currHash, result]);
} catch {}
}
return postcssCache.get(filePath)[1] || [source];
};
const esbuildCss = async (initialBuildOptions, stdin, filePath) => {
const source = stdin.contents;
const currHash = getHash(source);
const [cacheHash] = esbuildCache.get(filePath) || [];
if (currHash !== cacheHash) {
const finalCssBuildOptions =
typeof cssBuildOptions === 'function'
? cssBuildOptions(initialBuildOptions)
: cssBuildOptions;
const { assetNames } = initialBuildOptions;
const { outputFiles, metafile, errors, warnings } = await esbuild.build({
...initialBuildOptions,
stdin,
write: false,
bundle: true,
metafile: true,
entryNames: assetNames,
entryPoints: [],
plugins: [],
...finalCssBuildOptions,
});
if (errors?.length) {
return {
errors,
};
}
const { outputs } = metafile;
const watchFiles = Object.values(outputs).reduce((arr, { inputs }) => {
arr.push(...Object.keys(inputs).map((input) => path.resolve(input)));
return arr;
}, []);
const entry = Object.keys(outputs).find((out) => outputs[out].entryPoint);
const entryFile = outputFiles.find((file) =>
normalizePathSlashes(file.path).endsWith(normalizePathSlashes(entry))
);
const result = {
outputFiles,
entryFile,
watchFiles,
warnings,
};
esbuildCache.set(filePath, [currHash, result]);
}
return esbuildCache.get(filePath)[1];
};
return {
name: 'esbuild-plugin-styles',
async setup(build) {
const initBuildOptions = build.initialOptions;
const assetOutputFiles = new Set();
// move newly crete stub modules to 'css-processed' namespace
build.onResolve({ filter: /.*/ }, (args) => {
if (args.pluginData?.contents) {
return {
path: args.path,
namespace: 'css-processed',
pluginData: args.pluginData,
};
}
});
// resolve asset imports for generated css files
build.onResolve({ filter: /.*/, namespace: 'css-processed' }, async (args) =>
// since we don't consume them its fine that the imports are wrong
({
path: args.path,
external: true,
})
);
build.onLoad({ filter: include }, async (args) => {
const { namespace, pluginData, path: filePath } = args;
if (namespace === 'css-processed') {
const { contents } = pluginData;
return {
contents,
loader: 'copy',
resolveDir: path.dirname(args.path),
};
}
const isSass = sassFilesRegex.test(filePath);
const isCssMdoule = cssModulesRegex && cssModulesRegex.test(filePath);
const fileName = path.basename(filePath);
const resolveDir = path.dirname(filePath);
const css = await fs.promises.readFile(filePath);
const [source, cssModulesJson] = await transpilePostCss(
isSass ? transpileSass(css, filePath) : css,
filePath,
isCssMdoule
);
const { outputFiles, entryFile, watchFiles, warnings, errors } = await esbuildCss(
initBuildOptions,
{
contents: source,
sourcefile: fileName,
resolveDir,
loader: 'css',
},
filePath
);
if (errors) {
return {
errors,
};
}
const entryFilePath = normalizePathSlashes(
path.resolve(
path.dirname(entryFile.path),
`${path.basename(replaceExtension(args.path, '.css'))}`
)
);
outputFiles.forEach((file) => {
if (file !== entryFile) {
assetOutputFiles.add(file);
}
});
if (isCssMdoule) {
return {
contents: `
export { default as file } from ${JSON.stringify(entryFilePath)};
export default ${JSON.stringify(cssModulesJson)};
`,
resolveDir,
watchFiles,
warnings,
pluginData: {
contents: entryFile.contents,
},
};
}
return {
contents: `export { default } from ${JSON.stringify(entryFilePath)};`,
resolveDir,
watchFiles,
warnings,
pluginData: {
contents: entryFile.contents,
},
};
});
build.onEnd((result) => {
const { outputFiles } = result;
outputFiles.push(...Array.from(assetOutputFiles));
assetOutputFiles.clear();
});
},
};
};
+49
View File
@@ -0,0 +1,49 @@
import path from 'node:path';
import minimatch from 'minimatch';
import postcss from 'postcss';
import tailwindcss from 'tailwindcss';
import { resolveConfig } from '../resolveConfig.js';
export const esbuildPluginTailwind = ({
tailwindCssFileRegex = /tailwind.*\.css$/,
tailwindConfig = './tailwind.config.js',
} = {}) => ({
name: 'tailwind',
async setup(build) {
const resolvedTailwindConfig = await resolveConfig(tailwindConfig);
build.onEnd(async (result) => {
if (result) {
const { metafile, outputFiles } = result;
const { inputs } = metafile;
const tailwindFile = outputFiles.find(({ path: outputFilePath }) =>
tailwindCssFileRegex.test(outputFilePath)
);
if (tailwindFile) {
const { path: tailwindFilePath, text: tailwindFileCss } = tailwindFile;
const tailwindContentGlobs = (resolvedTailwindConfig?.content || []).filter(
(entry) => typeof entry === 'string'
);
const inputFilePaths = Object.keys(inputs).map((input) => path.resolve(input));
const includedFiles = Array.from(
new Set(
tailwindContentGlobs
.map((glob) => minimatch.match(inputFilePaths, glob, { dot: true }))
.flat()
)
);
const postcssResult = await postcss([
tailwindcss({ ...(resolvedTailwindConfig || {}), content: includedFiles }),
]).process(tailwindFileCss, {
from: tailwindFilePath,
});
tailwindFile.contents = Buffer.from(postcssResult.css);
}
}
});
},
});
+13
View File
@@ -0,0 +1,13 @@
import url from 'node:url';
import path from 'node:path';
export const resolveConfig = async (configPath) => {
if (configPath) {
const loadedConfig = (
await import(url.pathToFileURL(path.resolve(configPath))).catch(() => ({}))
).default;
if (loadedConfig) {
return loadedConfig;
}
}
};
+25
View File
@@ -0,0 +1,25 @@
import fs from 'node:fs';
import crypto from 'node:crypto';
import path from 'node:path';
const getHash = (content) => crypto.createHash('md5').update(content).digest('hex');
export const writeOnlyChanges = async (outputFiles, changesMap) => {
await Promise.all(
outputFiles.map(({ path: filepath }) =>
fs.promises.mkdir(path.dirname(filepath), { recursive: true })
)
);
await Promise.all(
outputFiles.map(({ path: filepath, contents }) => {
const currContentsHash = getHash(contents);
const cacheHash = changesMap.get(filepath);
if (cacheHash !== currContentsHash) {
changesMap.set(filepath, currContentsHash);
return fs.promises.writeFile(filepath, contents);
}
return null;
})
);
};
-1
View File
@@ -1,6 +1,5 @@
{
"private": true,
"name": "@~local/full-coverage",
"bin": {
"full-coverage": "./bin/generateFullCoverage.js"
},
-1
View File
@@ -1,6 +1,5 @@
{
"private": true,
"name": "@~local/playwright-tooling",
"main": "./src/index.js",
"bin": {
"playwright-merge-coverage": "./bin/mergeCoverage.js"
-1
View File
@@ -1,6 +1,5 @@
{
"private": true,
"name": "@~local/rollup",
"exports": {
".": "./src/createRollupConfig.js",
"./playwright": "./src/playwright/createPlaywrightRollupConfig.js"
+4
View File
@@ -0,0 +1,4 @@
{
"private": true,
"main": "tailwind.config.js"
}
+29
View File
@@ -0,0 +1,29 @@
const defaultTheme = require('tailwindcss/defaultTheme');
/** @type {import('tailwindcss').Config} */
module.exports = {
theme: {
extend: {},
container: {
center: true,
},
fontFamily: {
sans: ['Noto Sans', ...defaultTheme.fontFamily.sans],
},
fontWeight: {
normal: 400,
medium: 600,
bold: 800,
},
screens: {
xxs: '374px',
xs: '640px',
sm: '768px',
md: '960px',
lg: '1280px',
xl: '1440px',
xxl: '1536px',
},
},
plugins: [],
};
-1
View File
@@ -1,6 +1,5 @@
{
"private": true,
"name": "@~local/tsconfig",
"main": "./tsconfig.json",
"version": "0.0.0"
}