larry babby and threejs for glsl

This commit is contained in:
Sam
2024-06-24 21:24:00 +12:00
parent 87d5dc634d
commit 907ebae4c0
6474 changed files with 1279596 additions and 8 deletions

View File

@@ -0,0 +1,21 @@
MIT License
Copyright (c) 2017-present Devon Govett
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all
copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE.

View File

@@ -0,0 +1,347 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = void 0;
function _rust() {
const data = require("@parcel/rust");
_rust = function () {
return data;
};
return data;
}
function _utils() {
const data = require("@parcel/utils");
_utils = function () {
return data;
};
return data;
}
function _plugin() {
const data = require("@parcel/plugin");
_plugin = function () {
return data;
};
return data;
}
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _semver() {
const data = _interopRequireDefault(require("semver"));
_semver = function () {
return data;
};
return data;
}
function _postcssValueParser() {
const data = _interopRequireDefault(require("postcss-value-parser"));
_postcssValueParser = function () {
return data;
};
return data;
}
var _loadConfig = require("./loadConfig");
var _constants = require("./constants");
function _diagnostic() {
const data = require("@parcel/diagnostic");
_diagnostic = function () {
return data;
};
return data;
}
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
const LEGACY_MODULE_RE = /@value|:export|(:global|:local|:import)(?!\s*\()/i;
const MODULE_BY_NAME_RE = /\.module\./;
var _default = exports.default = new (_plugin().Transformer)({
loadConfig({
config,
options,
logger
}) {
return (0, _loadConfig.load)({
config,
options,
logger
});
},
canReuseAST({
ast
}) {
return ast.type === 'postcss' && _semver().default.satisfies(ast.version, _constants.POSTCSS_RANGE);
},
async parse({
asset,
config,
options
}) {
let isLegacy = await isLegacyCssModule(asset);
if (!config && !isLegacy) {
return;
}
const postcss = await loadPostcss(options, asset.filePath);
return {
type: 'postcss',
version: '8.2.1',
program: postcss.parse(await asset.getCode(), {
from: asset.filePath
}).toJSON()
};
},
async transform({
asset,
config,
options,
resolve,
logger
}) {
asset.type = 'css';
let isLegacy = await isLegacyCssModule(asset);
if (isLegacy && !config) {
config = {
raw: {},
filePath: '',
hydrated: {
plugins: [],
from: asset.filePath,
to: asset.filePath,
modules: {}
}
};
// TODO: warning?
}
if (!config) {
return [asset];
}
const postcss = await loadPostcss(options, asset.filePath);
let ast = (0, _nullthrows().default)(await asset.getAST());
let program = postcss.fromJSON(ast.program);
let plugins = [...config.hydrated.plugins];
let cssModules = null;
if (config.hydrated.modules) {
asset.meta.cssModulesCompiled = 'postcss';
let code = asset.isASTDirty() ? null : await asset.getCode();
if (Object.keys(config.hydrated.modules).length === 0 && code && !isLegacy && !LEGACY_MODULE_RE.test(code)) {
let filename = _path().default.basename(config.filePath);
let message;
let configKey;
let hint;
if (config.raw.modules) {
message = (0, _diagnostic().md)`The "modules" option in __${filename}__ can be replaced with configuration for @parcel/transformer-css to improve build performance.`;
configKey = '/modules';
hint = (0, _diagnostic().md)`Remove the "modules" option from __${filename}__`;
} else {
message = (0, _diagnostic().md)`The "postcss-modules" plugin in __${filename}__ can be replaced with configuration for @parcel/transformer-css to improve build performance.`;
configKey = '/plugins/postcss-modules';
hint = (0, _diagnostic().md)`Remove the "postcss-modules" plugin from __${filename}__`;
}
if (filename === 'package.json') {
configKey = `/postcss${configKey}`;
}
let hints = ['Enable the "cssModules" option for "@parcel/transformer-css" in your package.json'];
if (plugins.length === 0) {
message += (0, _diagnostic().md)` Since there are no other plugins, __${filename}__ can be deleted safely.`;
hints.push((0, _diagnostic().md)`Delete __${filename}__`);
} else {
hints.push(hint);
}
let codeFrames;
if (_path().default.extname(filename) !== '.js') {
let contents = await asset.fs.readFile(config.filePath, 'utf8');
codeFrames = [{
language: 'json',
filePath: config.filePath,
code: contents,
codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, [{
key: configKey,
type: 'key'
}])
}];
} else {
codeFrames = [{
filePath: config.filePath,
codeHighlights: [{
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 1
}
}]
}];
}
logger.warn({
message,
hints,
documentationURL: 'https://parceljs.org/languages/css/#enabling-css-modules-globally',
codeFrames
});
}
// TODO: should this be resolved from the project root?
let postcssModules = await options.packageManager.require('postcss-modules', asset.filePath, {
range: '^4.3.0',
saveDev: true,
shouldAutoInstall: options.shouldAutoInstall
});
plugins.push(postcssModules({
getJSON: (filename, json) => cssModules = json,
Loader: await createLoader(asset, resolve, options),
generateScopedName: (name, filename) => `${name}_${(0, _rust().hashString)(_path().default.relative(options.projectRoot, filename)).substr(0, 6)}`,
...config.hydrated.modules
}));
if (code == null || COMPOSES_RE.test(code)) {
program.walkDecls(decl => {
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
if (decl.prop === 'composes' && importPath != null) {
let parsed = (0, _postcssValueParser().default)(decl.value);
parsed.walk(node => {
if (node.type === 'string') {
asset.addDependency({
specifier: importPath,
specifierType: 'url',
loc: {
filePath: asset.filePath,
start: decl.source.start,
end: {
line: decl.source.start.line,
column: decl.source.start.column + importPath.length
}
}
});
}
});
}
});
}
}
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
let {
messages,
root
} = await postcss(plugins).process(program, config.hydrated);
asset.setAST({
type: 'postcss',
version: '8.2.1',
program: root.toJSON()
});
for (let msg of messages) {
if (msg.type === 'dependency') {
asset.invalidateOnFileChange(msg.file);
} else if (msg.type === 'dir-dependency') {
var _msg$glob;
let pattern = `${msg.dir}/${(_msg$glob = msg.glob) !== null && _msg$glob !== void 0 ? _msg$glob : '**/*'}`;
let files = await (0, _utils().glob)(pattern, asset.fs, {
onlyFiles: true
});
for (let file of files) {
asset.invalidateOnFileChange(_path().default.normalize(file));
}
asset.invalidateOnFileCreate({
glob: pattern
});
}
}
let assets = [asset];
if (cssModules) {
// $FlowFixMe
let cssModulesList = Object.entries(cssModules);
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
let code;
if (deps.length > 0) {
code = `
module.exports = Object.assign({}, ${deps.map(dep => `require(${JSON.stringify(dep.specifier)})`).join(', ')}, ${JSON.stringify(cssModules, null, 2)});
`;
} else {
code = cssModulesList.map(
// This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
([className, classNameHashed]) => `module.exports[${JSON.stringify(className)}] = ${JSON.stringify(classNameHashed)};`).join('\n');
}
asset.symbols.ensure();
for (let [k, v] of cssModulesList) {
asset.symbols.set(k, v);
}
asset.symbols.set('default', 'default');
assets.push({
type: 'js',
content: code
});
}
return assets;
},
async generate({
asset,
ast,
options
}) {
const postcss = await loadPostcss(options, asset.filePath);
let code = '';
postcss.stringify(postcss.fromJSON(ast.program), c => {
code += c;
});
return {
content: code
};
}
});
async function createLoader(asset, resolve, options) {
let {
default: FileSystemLoader
} = await options.packageManager.require('postcss-modules/build/css-loader-core/loader', asset.filePath);
return class extends FileSystemLoader {
async fetch(composesPath, relativeTo) {
let importPath = composesPath.replace(/^["']|["']$/g, '');
let resolved = await resolve(relativeTo, importPath);
let rootRelativePath = _path().default.resolve(_path().default.dirname(relativeTo), resolved);
let root = _path().default.resolve('/');
// fixes an issue on windows which is part of the css-modules-loader-core
// see https://github.com/css-modules/css-modules-loader-core/issues/230
if (rootRelativePath.startsWith(root)) {
rootRelativePath = rootRelativePath.substr(root.length);
}
let source = await asset.fs.readFile(resolved, 'utf-8');
let {
exportTokens
} = await this.core.load(source, rootRelativePath, undefined,
// $FlowFixMe[method-unbinding]
this.fetch.bind(this));
return exportTokens;
}
get finalSource() {
return '';
}
};
}
function loadPostcss(options, from) {
return options.packageManager.require('postcss', from, {
range: _constants.POSTCSS_RANGE,
saveDev: true,
shouldAutoInstall: options.shouldAutoInstall
});
}
async function isLegacyCssModule(asset) {
if (!MODULE_BY_NAME_RE.test(asset.filePath)) {
return false;
}
let code = await asset.getCode();
return LEGACY_MODULE_RE.test(code);
}

View File

@@ -0,0 +1,7 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.POSTCSS_RANGE = void 0;
const POSTCSS_RANGE = exports.POSTCSS_RANGE = '^8.2.1';

View File

@@ -0,0 +1,162 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.load = load;
function _path() {
const data = _interopRequireDefault(require("path"));
_path = function () {
return data;
};
return data;
}
function _diagnostic() {
const data = require("@parcel/diagnostic");
_diagnostic = function () {
return data;
};
return data;
}
function _nullthrows() {
const data = _interopRequireDefault(require("nullthrows"));
_nullthrows = function () {
return data;
};
return data;
}
function _clone() {
const data = _interopRequireDefault(require("clone"));
_clone = function () {
return data;
};
return data;
}
var _constants = require("./constants");
var _loadPlugins = _interopRequireDefault(require("./loadPlugins"));
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
async function configHydrator(configFile, config, resolveFrom, options, logger) {
if (configFile == null) {
return;
}
// Load the custom config...
let modulesConfig;
let configFilePlugins = (0, _clone().default)(configFile.plugins);
if (configFilePlugins != null && typeof configFilePlugins === 'object' && configFilePlugins['postcss-modules'] != null) {
modulesConfig = configFilePlugins['postcss-modules'];
delete configFilePlugins['postcss-modules'];
}
if (!modulesConfig && configFile.modules) {
modulesConfig = {};
}
let plugins = await (0, _loadPlugins.default)(configFilePlugins, (0, _nullthrows().default)(resolveFrom), options);
// contents is either:
// from JSON: { plugins: { 'postcss-foo': { ...opts } } }
// from JS (v8): { plugins: [ { postcssPlugin: 'postcss-foo', ...visitor callback functions } ]
// from JS (v7): { plugins: [ [Function: ...] ]
let pluginArray = Array.isArray(configFilePlugins) ? configFilePlugins : Object.keys(configFilePlugins);
for (let p of pluginArray) {
if (typeof p === 'string') {
config.addDevDependency({
specifier: p,
resolveFrom: (0, _nullthrows().default)(resolveFrom)
});
}
}
let redundantPlugins = pluginArray.filter(p => p === 'autoprefixer' || p === 'postcss-preset-env');
if (redundantPlugins.length > 0) {
let filename = _path().default.basename(resolveFrom);
let isPackageJson = filename === 'package.json';
let message;
let hints = [];
if (!isPackageJson && redundantPlugins.length === pluginArray.length) {
message = (0, _diagnostic().md)`Parcel includes CSS transpilation and vendor prefixing by default. PostCSS config __${filename}__ contains only redundant plugins. Deleting it may significantly improve build performance.`;
hints.push((0, _diagnostic().md)`Delete __${filename}__`);
} else {
message = (0, _diagnostic().md)`Parcel includes CSS transpilation and vendor prefixing by default. PostCSS config __${filename}__ contains the following redundant plugins: ${[...redundantPlugins].map(p => _diagnostic().md.underline(p))}. Removing these may improve build performance.`;
hints.push((0, _diagnostic().md)`Remove the above plugins from __${filename}__`);
}
let codeFrames;
if (_path().default.extname(filename) !== '.js') {
let contents = await options.inputFS.readFile(resolveFrom, 'utf8');
let prefix = isPackageJson ? '/postcss' : '';
codeFrames = [{
language: 'json',
filePath: resolveFrom,
code: contents,
codeHighlights: (0, _diagnostic().generateJSONCodeHighlights)(contents, redundantPlugins.map(plugin => ({
key: `${prefix}/plugins/${plugin}`,
type: 'key'
})))
}];
} else {
codeFrames = [{
filePath: resolveFrom,
codeHighlights: [{
start: {
line: 1,
column: 1
},
end: {
line: 1,
column: 1
}
}]
}];
}
logger.warn({
message,
hints,
documentationURL: 'https://parceljs.org/languages/css/#default-plugins',
codeFrames
});
}
return {
raw: configFile,
filePath: resolveFrom,
hydrated: {
plugins,
from: config.searchPath,
to: config.searchPath,
modules: modulesConfig
}
};
}
async function load({
config,
options,
logger
}) {
if (!config.isSource) {
return;
}
let configFile = await config.getConfig(['.postcssrc', '.postcssrc.json', '.postcssrc.js', '.postcssrc.cjs', '.postcssrc.mjs', 'postcss.config.js', 'postcss.config.cjs', 'postcss.config.mjs'], {
packageKey: 'postcss'
});
let contents = null;
if (configFile) {
config.addDevDependency({
specifier: 'postcss',
resolveFrom: config.searchPath,
range: _constants.POSTCSS_RANGE
});
contents = configFile.contents;
let isDynamic = configFile && _path().default.extname(configFile.filePath).endsWith('js');
if (isDynamic) {
// We have to invalidate on startup in case the config is non-deterministic,
// e.g. using unknown environment variables, reading from the filesystem, etc.
logger.warn({
message: 'WARNING: Using a JavaScript PostCSS config file means losing out on caching features of Parcel. Use a .postcssrc(.json) file whenever possible.'
});
}
if (typeof contents !== 'object') {
throw new Error('PostCSS config should be an object.');
}
if (contents.plugins == null || typeof contents.plugins !== 'object' || Object.keys(contents.plugins).length === 0) {
throw new Error('PostCSS config must have plugins');
}
}
return configHydrator(contents, config, configFile === null || configFile === void 0 ? void 0 : configFile.filePath, options, logger);
}

View File

@@ -0,0 +1,29 @@
"use strict";
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.default = loadExternalPlugins;
async function loadExternalPlugins(plugins, relative, options) {
if (Array.isArray(plugins)) {
return Promise.all(plugins.map(p => loadPlugin(p, relative, null, options.packageManager, options.shouldAutoInstall)).filter(Boolean));
} else if (typeof plugins === 'object') {
let mapPlugins = await Promise.all(Object.keys(plugins).map(p => loadPlugin(p, relative, plugins[p], options.packageManager, options.shouldAutoInstall)));
return mapPlugins.filter(Boolean);
} else {
return [];
}
}
async function loadPlugin(pluginArg, relative, options = {}, packageManager, shouldAutoInstall) {
if (typeof pluginArg !== 'string') {
return pluginArg;
}
let plugin = await packageManager.require(pluginArg, relative, {
shouldAutoInstall
});
plugin = plugin.default || plugin;
if (options != null && typeof options === 'object' && Object.keys(options).length > 0) {
plugin = plugin(options);
}
return plugin.default || plugin;
}

View File

@@ -0,0 +1,37 @@
{
"name": "@parcel/transformer-postcss",
"version": "2.12.0",
"license": "MIT",
"publishConfig": {
"access": "public"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/parcel"
},
"repository": {
"type": "git",
"url": "https://github.com/parcel-bundler/parcel.git"
},
"main": "lib/PostCSSTransformer.js",
"source": "src/PostCSSTransformer.js",
"engines": {
"node": ">= 12.0.0",
"parcel": "^2.12.0"
},
"dependencies": {
"@parcel/diagnostic": "2.12.0",
"@parcel/plugin": "2.12.0",
"@parcel/rust": "2.12.0",
"@parcel/utils": "2.12.0",
"clone": "^2.1.1",
"nullthrows": "^1.1.1",
"postcss-value-parser": "^4.2.0",
"semver": "^7.5.2"
},
"devDependencies": {
"postcss": "^8.4.5",
"postcss-modules": "^4.3.1"
},
"gitHead": "2059029ee91e5f03a273b0954d3e629d7375f986"
}

View File

@@ -0,0 +1,337 @@
// @flow
import type {FilePath, Asset, MutableAsset, PluginOptions} from '@parcel/types';
import {hashString} from '@parcel/rust';
import {glob} from '@parcel/utils';
import {Transformer} from '@parcel/plugin';
import nullthrows from 'nullthrows';
import path from 'path';
import semver from 'semver';
import valueParser from 'postcss-value-parser';
import typeof * as Postcss from 'postcss';
import {load} from './loadConfig';
import {POSTCSS_RANGE} from './constants';
import {md, generateJSONCodeHighlights} from '@parcel/diagnostic';
const COMPOSES_RE = /composes:.+from\s*("|').*("|')\s*;?/;
const FROM_IMPORT_RE = /.+from\s*(?:"|')(.*)(?:"|')\s*;?/;
const LEGACY_MODULE_RE = /@value|:export|(:global|:local|:import)(?!\s*\()/i;
const MODULE_BY_NAME_RE = /\.module\./;
export default (new Transformer({
loadConfig({config, options, logger}) {
return load({config, options, logger});
},
canReuseAST({ast}) {
return (
ast.type === 'postcss' && semver.satisfies(ast.version, POSTCSS_RANGE)
);
},
async parse({asset, config, options}) {
let isLegacy = await isLegacyCssModule(asset);
if (!config && !isLegacy) {
return;
}
const postcss = await loadPostcss(options, asset.filePath);
return {
type: 'postcss',
version: '8.2.1',
program: postcss
.parse(await asset.getCode(), {
from: asset.filePath,
})
.toJSON(),
};
},
async transform({asset, config, options, resolve, logger}) {
asset.type = 'css';
let isLegacy = await isLegacyCssModule(asset);
if (isLegacy && !config) {
config = {
raw: {},
filePath: '',
hydrated: {
plugins: [],
from: asset.filePath,
to: asset.filePath,
modules: {},
},
};
// TODO: warning?
}
if (!config) {
return [asset];
}
const postcss: Postcss = await loadPostcss(options, asset.filePath);
let ast = nullthrows(await asset.getAST());
let program = postcss.fromJSON(ast.program);
let plugins = [...config.hydrated.plugins];
let cssModules: ?{|[string]: string|} = null;
if (config.hydrated.modules) {
asset.meta.cssModulesCompiled = 'postcss';
let code = asset.isASTDirty() ? null : await asset.getCode();
if (
Object.keys(config.hydrated.modules).length === 0 &&
code &&
!isLegacy &&
!LEGACY_MODULE_RE.test(code)
) {
let filename = path.basename(config.filePath);
let message;
let configKey;
let hint;
if (config.raw.modules) {
message = md`The "modules" option in __${filename}__ can be replaced with configuration for @parcel/transformer-css to improve build performance.`;
configKey = '/modules';
hint = md`Remove the "modules" option from __${filename}__`;
} else {
message = md`The "postcss-modules" plugin in __${filename}__ can be replaced with configuration for @parcel/transformer-css to improve build performance.`;
configKey = '/plugins/postcss-modules';
hint = md`Remove the "postcss-modules" plugin from __${filename}__`;
}
if (filename === 'package.json') {
configKey = `/postcss${configKey}`;
}
let hints = [
'Enable the "cssModules" option for "@parcel/transformer-css" in your package.json',
];
if (plugins.length === 0) {
message += md` Since there are no other plugins, __${filename}__ can be deleted safely.`;
hints.push(md`Delete __${filename}__`);
} else {
hints.push(hint);
}
let codeFrames;
if (path.extname(filename) !== '.js') {
let contents = await asset.fs.readFile(config.filePath, 'utf8');
codeFrames = [
{
language: 'json',
filePath: config.filePath,
code: contents,
codeHighlights: generateJSONCodeHighlights(contents, [
{
key: configKey,
type: 'key',
},
]),
},
];
} else {
codeFrames = [
{
filePath: config.filePath,
codeHighlights: [
{
start: {line: 1, column: 1},
end: {line: 1, column: 1},
},
],
},
];
}
logger.warn({
message,
hints,
documentationURL:
'https://parceljs.org/languages/css/#enabling-css-modules-globally',
codeFrames,
});
}
// TODO: should this be resolved from the project root?
let postcssModules = await options.packageManager.require(
'postcss-modules',
asset.filePath,
{
range: '^4.3.0',
saveDev: true,
shouldAutoInstall: options.shouldAutoInstall,
},
);
plugins.push(
postcssModules({
getJSON: (filename, json) => (cssModules = json),
Loader: await createLoader(asset, resolve, options),
generateScopedName: (name, filename) =>
`${name}_${hashString(
path.relative(options.projectRoot, filename),
).substr(0, 6)}`,
...config.hydrated.modules,
}),
);
if (code == null || COMPOSES_RE.test(code)) {
program.walkDecls(decl => {
let [, importPath] = FROM_IMPORT_RE.exec(decl.value) || [];
if (decl.prop === 'composes' && importPath != null) {
let parsed = valueParser(decl.value);
parsed.walk(node => {
if (node.type === 'string') {
asset.addDependency({
specifier: importPath,
specifierType: 'url',
loc: {
filePath: asset.filePath,
start: decl.source.start,
end: {
line: decl.source.start.line,
column: decl.source.start.column + importPath.length,
},
},
});
}
});
}
});
}
}
// $FlowFixMe Added in Flow 0.121.0 upgrade in #4381
let {messages, root} = await postcss(plugins).process(
program,
config.hydrated,
);
asset.setAST({
type: 'postcss',
version: '8.2.1',
program: root.toJSON(),
});
for (let msg of messages) {
if (msg.type === 'dependency') {
asset.invalidateOnFileChange(msg.file);
} else if (msg.type === 'dir-dependency') {
let pattern = `${msg.dir}/${msg.glob ?? '**/*'}`;
let files = await glob(pattern, asset.fs, {onlyFiles: true});
for (let file of files) {
asset.invalidateOnFileChange(path.normalize(file));
}
asset.invalidateOnFileCreate({glob: pattern});
}
}
let assets = [asset];
if (cssModules) {
// $FlowFixMe
let cssModulesList = (Object.entries(cssModules): Array<
[string, string],
>);
let deps = asset.getDependencies().filter(dep => dep.priority === 'sync');
let code: string;
if (deps.length > 0) {
code = `
module.exports = Object.assign({}, ${deps
.map(dep => `require(${JSON.stringify(dep.specifier)})`)
.join(', ')}, ${JSON.stringify(cssModules, null, 2)});
`;
} else {
code = cssModulesList
.map(
// This syntax enables shaking the invidual statements, so that unused classes don't even exist in JS.
([className, classNameHashed]) =>
`module.exports[${JSON.stringify(className)}] = ${JSON.stringify(
classNameHashed,
)};`,
)
.join('\n');
}
asset.symbols.ensure();
for (let [k, v] of cssModulesList) {
asset.symbols.set(k, v);
}
asset.symbols.set('default', 'default');
assets.push({
type: 'js',
content: code,
});
}
return assets;
},
async generate({asset, ast, options}) {
const postcss: Postcss = await loadPostcss(options, asset.filePath);
let code = '';
postcss.stringify(postcss.fromJSON(ast.program), c => {
code += c;
});
return {
content: code,
};
},
}): Transformer);
async function createLoader(
asset: MutableAsset,
resolve: (from: FilePath, to: string) => Promise<FilePath>,
options: PluginOptions,
) {
let {default: FileSystemLoader} = await options.packageManager.require(
'postcss-modules/build/css-loader-core/loader',
asset.filePath,
);
return class ParcelFileSystemLoader extends FileSystemLoader {
async fetch(composesPath, relativeTo) {
let importPath = composesPath.replace(/^["']|["']$/g, '');
let resolved = await resolve(relativeTo, importPath);
let rootRelativePath = path.resolve(path.dirname(relativeTo), resolved);
let root = path.resolve('/');
// fixes an issue on windows which is part of the css-modules-loader-core
// see https://github.com/css-modules/css-modules-loader-core/issues/230
if (rootRelativePath.startsWith(root)) {
rootRelativePath = rootRelativePath.substr(root.length);
}
let source = await asset.fs.readFile(resolved, 'utf-8');
let {exportTokens} = await this.core.load(
source,
rootRelativePath,
undefined,
// $FlowFixMe[method-unbinding]
this.fetch.bind(this),
);
return exportTokens;
}
get finalSource() {
return '';
}
};
}
function loadPostcss(options: PluginOptions, from: FilePath): Promise<Postcss> {
return options.packageManager.require('postcss', from, {
range: POSTCSS_RANGE,
saveDev: true,
shouldAutoInstall: options.shouldAutoInstall,
});
}
async function isLegacyCssModule(asset: Asset | MutableAsset) {
if (!MODULE_BY_NAME_RE.test(asset.filePath)) {
return false;
}
let code = await asset.getCode();
return LEGACY_MODULE_RE.test(code);
}

View File

@@ -0,0 +1,3 @@
// @flow
export const POSTCSS_RANGE = '^8.2.1';

View File

@@ -0,0 +1,215 @@
// @flow
import type {
Config,
FilePath,
PluginOptions,
PluginLogger,
} from '@parcel/types';
import path from 'path';
import {md, generateJSONCodeHighlights} from '@parcel/diagnostic';
import nullthrows from 'nullthrows';
import clone from 'clone';
import {POSTCSS_RANGE} from './constants';
import loadExternalPlugins from './loadPlugins';
type ConfigResult = {|
raw: any,
filePath: string,
hydrated: {|
plugins: Array<any>,
from: FilePath,
to: FilePath,
modules: any,
|},
|};
async function configHydrator(
configFile: any,
config: Config,
resolveFrom: FilePath,
options: PluginOptions,
logger: PluginLogger,
): Promise<?ConfigResult> {
if (configFile == null) {
return;
}
// Load the custom config...
let modulesConfig;
let configFilePlugins = clone(configFile.plugins);
if (
configFilePlugins != null &&
typeof configFilePlugins === 'object' &&
configFilePlugins['postcss-modules'] != null
) {
modulesConfig = configFilePlugins['postcss-modules'];
delete configFilePlugins['postcss-modules'];
}
if (!modulesConfig && configFile.modules) {
modulesConfig = {};
}
let plugins = await loadExternalPlugins(
configFilePlugins,
nullthrows(resolveFrom),
options,
);
// contents is either:
// from JSON: { plugins: { 'postcss-foo': { ...opts } } }
// from JS (v8): { plugins: [ { postcssPlugin: 'postcss-foo', ...visitor callback functions } ]
// from JS (v7): { plugins: [ [Function: ...] ]
let pluginArray = Array.isArray(configFilePlugins)
? configFilePlugins
: Object.keys(configFilePlugins);
for (let p of pluginArray) {
if (typeof p === 'string') {
config.addDevDependency({
specifier: p,
resolveFrom: nullthrows(resolveFrom),
});
}
}
let redundantPlugins = pluginArray.filter(
p => p === 'autoprefixer' || p === 'postcss-preset-env',
);
if (redundantPlugins.length > 0) {
let filename = path.basename(resolveFrom);
let isPackageJson = filename === 'package.json';
let message;
let hints = [];
if (!isPackageJson && redundantPlugins.length === pluginArray.length) {
message = md`Parcel includes CSS transpilation and vendor prefixing by default. PostCSS config __${filename}__ contains only redundant plugins. Deleting it may significantly improve build performance.`;
hints.push(md`Delete __${filename}__`);
} else {
message = md`Parcel includes CSS transpilation and vendor prefixing by default. PostCSS config __${filename}__ contains the following redundant plugins: ${[
...redundantPlugins,
].map(p =>
md.underline(p),
)}. Removing these may improve build performance.`;
hints.push(md`Remove the above plugins from __${filename}__`);
}
let codeFrames;
if (path.extname(filename) !== '.js') {
let contents = await options.inputFS.readFile(resolveFrom, 'utf8');
let prefix = isPackageJson ? '/postcss' : '';
codeFrames = [
{
language: 'json',
filePath: resolveFrom,
code: contents,
codeHighlights: generateJSONCodeHighlights(
contents,
redundantPlugins.map(plugin => ({
key: `${prefix}/plugins/${plugin}`,
type: 'key',
})),
),
},
];
} else {
codeFrames = [
{
filePath: resolveFrom,
codeHighlights: [
{
start: {line: 1, column: 1},
end: {line: 1, column: 1},
},
],
},
];
}
logger.warn({
message,
hints,
documentationURL: 'https://parceljs.org/languages/css/#default-plugins',
codeFrames,
});
}
return {
raw: configFile,
filePath: resolveFrom,
hydrated: {
plugins,
from: config.searchPath,
to: config.searchPath,
modules: modulesConfig,
},
};
}
export async function load({
config,
options,
logger,
}: {|
config: Config,
options: PluginOptions,
logger: PluginLogger,
|}): Promise<?ConfigResult> {
if (!config.isSource) {
return;
}
let configFile: any = await config.getConfig(
[
'.postcssrc',
'.postcssrc.json',
'.postcssrc.js',
'.postcssrc.cjs',
'.postcssrc.mjs',
'postcss.config.js',
'postcss.config.cjs',
'postcss.config.mjs',
],
{packageKey: 'postcss'},
);
let contents = null;
if (configFile) {
config.addDevDependency({
specifier: 'postcss',
resolveFrom: config.searchPath,
range: POSTCSS_RANGE,
});
contents = configFile.contents;
let isDynamic =
configFile && path.extname(configFile.filePath).endsWith('js');
if (isDynamic) {
// We have to invalidate on startup in case the config is non-deterministic,
// e.g. using unknown environment variables, reading from the filesystem, etc.
logger.warn({
message:
'WARNING: Using a JavaScript PostCSS config file means losing out on caching features of Parcel. Use a .postcssrc(.json) file whenever possible.',
});
}
if (typeof contents !== 'object') {
throw new Error('PostCSS config should be an object.');
}
if (
contents.plugins == null ||
typeof contents.plugins !== 'object' ||
Object.keys(contents.plugins).length === 0
) {
throw new Error('PostCSS config must have plugins');
}
}
return configHydrator(
contents,
config,
configFile?.filePath,
options,
logger,
);
}

View File

@@ -0,0 +1,69 @@
// @flow
import type {FilePath, PluginOptions} from '@parcel/types';
import type {PackageManager} from '@parcel/package-manager';
export default async function loadExternalPlugins(
plugins: Array<string> | {|+[pluginName: string]: mixed|},
relative: FilePath,
options: PluginOptions,
): Promise<Array<mixed>> {
if (Array.isArray(plugins)) {
return Promise.all(
plugins
.map(p =>
loadPlugin(
p,
relative,
null,
options.packageManager,
options.shouldAutoInstall,
),
)
.filter(Boolean),
);
} else if (typeof plugins === 'object') {
let _plugins = plugins;
let mapPlugins = await Promise.all(
Object.keys(plugins).map(p =>
loadPlugin(
p,
relative,
_plugins[p],
options.packageManager,
options.shouldAutoInstall,
),
),
);
return mapPlugins.filter(Boolean);
} else {
return [];
}
}
async function loadPlugin(
pluginArg: string | Function,
relative: FilePath,
options: mixed = {},
packageManager: PackageManager,
shouldAutoInstall: boolean,
): mixed {
if (typeof pluginArg !== 'string') {
return pluginArg;
}
let plugin = await packageManager.require(pluginArg, relative, {
shouldAutoInstall,
});
plugin = plugin.default || plugin;
if (
options != null &&
typeof options === 'object' &&
Object.keys(options).length > 0
) {
plugin = plugin(options);
}
return plugin.default || plugin;
}