Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

First-class MDX support #10064

Merged
merged 6 commits into from
Jan 5, 2025
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
226 changes: 118 additions & 108 deletions Cargo.lock

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion crates/macros/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ napi = ["dep:napi", "dep:napi-derive", "dep:crossbeam-channel"]

[dependencies]
indexmap = "1.9.2"
swc_core = { version = "6.0.1", features = [
swc_core = { version = "9", features = [
"common",
"common_ahash",
"common_sourcemap",
Expand Down
2 changes: 1 addition & 1 deletion crates/macros/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ pub enum JsValue {
}

pub struct Evaluator<'a> {
constants: HashMap<Id, Result<JsValue, Span>>,
pub constants: HashMap<Id, Result<JsValue, Span>>,
source_map: &'a SourceMap,
}

Expand Down
8 changes: 7 additions & 1 deletion packages/configs/default/index.json
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,13 @@
"@parcel/transformer-worklet",
"..."
],
"*.mdx": [
// For backward compatibility, include the old transformer
// so it is used if already installed in the project.
// Otherwise, the JS transformer will handle MDX.
"@parcel/transformer-mdx",
"@parcel/transformer-js"
],
"*.{js,mjs,jsm,jsx,es6,cjs,ts,tsx}": [
"@parcel/transformer-babel",
"@parcel/transformer-js",
Expand All @@ -33,7 +40,6 @@
"*.pug": ["@parcel/transformer-pug"],
"*.coffee": ["@parcel/transformer-coffeescript"],
"*.elm": ["@parcel/transformer-elm"],
"*.mdx": ["@parcel/transformer-mdx"],
"*.vue": ["@parcel/transformer-vue"],
"template:*.vue": ["@parcel/transformer-vue"],
"script:*.vue": ["@parcel/transformer-vue"],
Expand Down
4 changes: 3 additions & 1 deletion packages/configs/default/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,6 @@
"@parcel/transformer-inline-string": "2.13.3",
"@parcel/transformer-jsonld": "2.13.3",
"@parcel/transformer-less": "2.13.3",
"@parcel/transformer-mdx": "2.13.3",
"@parcel/transformer-pug": "2.13.3",
"@parcel/transformer-sass": "2.13.3",
"@parcel/transformer-stylus": "2.13.3",
Expand All @@ -76,6 +75,9 @@
"@parcel/transformer-xml": "2.13.3",
"@parcel/transformer-yaml": "2.13.3"
},
"optionalParcelDependencies": [
"@parcel/transformer-mdx"
],
"peerDependencies": {
"@parcel/core": "^2.13.3"
}
Expand Down
7 changes: 6 additions & 1 deletion packages/configs/default/test/config.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,9 @@

import assert from 'assert';

import config from '../';
import packageJson from '../package.json';
import json5 from 'json5';
import fs from 'fs';

describe('@parcel/config-default', () => {
let packageJsonDependencyNames: Set<string>;
Expand All @@ -13,7 +14,11 @@ describe('@parcel/config-default', () => {
packageJsonDependencyNames = new Set([
...Object.keys(packageJson.dependencies || {}),
...Object.keys(packageJson.parcelDependencies || {}),
...packageJson.optionalParcelDependencies,
]);
let config = json5.parse(
fs.readFileSync(__dirname + '/../index.json', 'utf8'),
);
configPackageReferences = collectConfigPackageReferences(config);
});

Expand Down
18 changes: 12 additions & 6 deletions packages/core/core/src/ParcelConfig.js
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import ThrowableDiagnostic, {
generateJSONCodeHighlights,
} from '@parcel/diagnostic';
import json5 from 'json5';
import nullthrows from 'nullthrows';

import {globToRegex} from '@parcel/utils';
import {basename} from 'path';
Expand Down Expand Up @@ -121,7 +122,7 @@ export default class ParcelConfig {
version: Semver,
resolveFrom: ProjectPath,
range: ?SemverRange,
|}> {
|} | null> {
let plugin = this.pluginCache.get(node.packageName);
if (plugin) {
return plugin;
Expand All @@ -138,8 +139,11 @@ export default class ParcelConfig {
return plugin;
}

async loadPlugin<T>(node: ParcelPluginNode): Promise<LoadedPlugin<T>> {
async loadPlugin<T>(node: ParcelPluginNode): Promise<LoadedPlugin<T> | null> {
let plugin = await this._loadPlugin(node);
if (!plugin) {
return null;
}
return {
...plugin,
name: node.packageName,
Expand All @@ -151,10 +155,12 @@ export default class ParcelConfig {
this.pluginCache.delete(packageName);
}

loadPlugins<T>(
async loadPlugins<T>(
plugins: PureParcelConfigPipeline,
): Promise<Array<LoadedPlugin<T>>> {
return Promise.all(plugins.map(p => this.loadPlugin<T>(p)));
return (await Promise.all(plugins.map(p => this.loadPlugin<T>(p)))).filter(
Boolean,
);
}

async getResolvers(): Promise<Array<LoadedPlugin<Resolver<mixed>>>> {
Expand Down Expand Up @@ -228,7 +234,7 @@ export default class ParcelConfig {
);
}

return this.loadPlugin<Bundler<mixed>>(this.bundler);
return nullthrows(await this.loadPlugin<Bundler<mixed>>(this.bundler));
}

async getNamers(): Promise<Array<LoadedPlugin<Namer<mixed>>>> {
Expand Down Expand Up @@ -265,7 +271,7 @@ export default class ParcelConfig {
'/packagers',
);
}
return this.loadPlugin<Packager<mixed, mixed>>(packager);
return nullthrows(await this.loadPlugin<Packager<mixed, mixed>>(packager));
}

_getOptimizerNodes(
Expand Down
5 changes: 5 additions & 0 deletions packages/core/core/src/Transformation.js
Original file line number Diff line number Diff line change
Expand Up @@ -569,6 +569,11 @@ export default class Transformation {
configKeyPath?: string,
parcelConfig: ParcelConfig,
): Promise<$ReadOnlyArray<TransformerResult | UncommittedAsset>> {
if (asset.transformers.has(transformerName)) {
return [asset];
}
asset.transformers.add(transformerName);

const logger = new PluginLogger({origin: transformerName});
const tracer = new PluginTracer({
origin: transformerName,
Expand Down
2 changes: 2 additions & 0 deletions packages/core/core/src/UncommittedAsset.js
Original file line number Diff line number Diff line change
Expand Up @@ -55,6 +55,7 @@ export default class UncommittedAsset {
idBase: ?string;
invalidations: Invalidations;
generate: ?() => Promise<GenerateOutput>;
transformers: Set<string>;

constructor({
value,
Expand All @@ -74,6 +75,7 @@ export default class UncommittedAsset {
this.isASTDirty = isASTDirty || false;
this.idBase = idBase;
this.invalidations = invalidations || createInvalidations();
this.transformers = new Set();
}

/*
Expand Down
16 changes: 9 additions & 7 deletions packages/core/core/src/assetUtils.js
Original file line number Diff line number Diff line change
Expand Up @@ -152,14 +152,16 @@ async function _generateFromAST(asset: CommittedAsset | UncommittedAsset) {
}

let pluginName = nullthrows(asset.value.plugin);
let {plugin} = await loadPlugin<Transformer<mixed>>(
pluginName,
fromProjectPath(
asset.options.projectRoot,
nullthrows(asset.value.configPath),
let {plugin} = nullthrows(
await loadPlugin<Transformer<mixed>>(
pluginName,
fromProjectPath(
asset.options.projectRoot,
nullthrows(asset.value.configPath),
),
nullthrows(asset.value.configKeyPath),
asset.options,
),
nullthrows(asset.value.configKeyPath),
asset.options,
);
let generate = plugin.generate?.bind(plugin);
if (!generate) {
Expand Down
20 changes: 16 additions & 4 deletions packages/core/core/src/loadParcelPlugin.js
Original file line number Diff line number Diff line change
Expand Up @@ -31,10 +31,15 @@ export default async function loadPlugin<T>(
version: Semver,
resolveFrom: ProjectPath,
range: ?SemverRange,
|}> {
|} | null> {
let resolveFrom = configPath;
let range;
if (resolveFrom.includes(NODE_MODULES)) {
let isOptional = false;
if (
resolveFrom.includes(NODE_MODULES) ||
(process.env.PARCEL_BUILD_ENV !== 'production' &&
/packages[/\\]configs/.test(resolveFrom))
) {
// Config packages can reference plugins, but cannot contain other plugins within them.
// This forces every published plugin to be published separately so they can be mixed and matched if needed.
if (pluginName.startsWith('.')) {
Expand Down Expand Up @@ -75,8 +80,11 @@ export default async function loadPlugin<T>(
// If not in the config's dependencies, the plugin will be auto installed with
// the version declared in "parcelDependencies".
range = configPkg.config.parcelDependencies?.[pluginName];
isOptional =
Array.isArray(configPkg.config.optionalParcelDependencies) &&
configPkg.config.optionalParcelDependencies.includes(pluginName);

if (range == null) {
if (range == null && !isOptional) {
let contents = await options.inputFS.readFile(
configPkg.files[0].filePath,
'utf8',
Expand Down Expand Up @@ -122,7 +130,7 @@ export default async function loadPlugin<T>(
pluginName,
resolveFrom,
{
shouldAutoInstall: options.shouldAutoInstall,
shouldAutoInstall: options.shouldAutoInstall && !isOptional,
range,
},
));
Expand All @@ -131,6 +139,10 @@ export default async function loadPlugin<T>(
throw err;
}

if (isOptional) {
return null;
}

let configContents = await options.inputFS.readFile(configPath, 'utf8');
let alternatives = await findAlternativeNodeModules(
options.inputFS,
Expand Down
13 changes: 8 additions & 5 deletions packages/core/core/test/ParcelConfig.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {inputFS} from '@parcel/test-utils';
import {parseAndProcessConfig} from '../src/requests/ParcelConfigRequest';
import {DEFAULT_OPTIONS} from './test-utils';
import {toProjectPath} from '../src/projectPath';
import nullthrows from 'nullthrows';

const PARCELRC_PATH = toProjectPath('/', '/.parcelrc');

Expand Down Expand Up @@ -148,11 +149,13 @@ describe('ParcelConfig', () => {
);

let warnStub = sinon.stub(logger, 'warn');
let {plugin} = await config.loadPlugin({
packageName: 'parcel-transformer-no-engines',
resolveFrom: configFilePath,
keyPath: '/transformers/*.js/0',
});
let {plugin} = nullthrows(
await config.loadPlugin({
packageName: 'parcel-transformer-no-engines',
resolveFrom: configFilePath,
keyPath: '/transformers/*.js/0',
}),
);
assert(plugin);
assert.equal(typeof plugin.transform, 'function');
assert(warnStub.calledOnce);
Expand Down
4 changes: 3 additions & 1 deletion packages/core/core/test/ParcelConfigRequest.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ import {
import {validatePackageName} from '../src/ParcelConfig.schema';
import {DEFAULT_OPTIONS, relative} from './test-utils';
import {toProjectPath} from '../src/projectPath';
import json5 from 'json5';
import fs from 'fs';

describe('ParcelConfigRequest', () => {
describe('validatePackageName', () => {
Expand Down Expand Up @@ -696,7 +698,7 @@ describe('ParcelConfigRequest', () => {
let defaultConfigPath = require.resolve('@parcel/config-default');
let defaultConfig = await processConfig(
{
...require('@parcel/config-default'),
...json5.parse(fs.readFileSync(defaultConfigPath, 'utf8')),
filePath: defaultConfigPath,
},
DEFAULT_OPTIONS,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"dependencies": {
"react": "*"
"react": "^19"
}
}
Loading
Loading