Skip to content

Commit

Permalink
feat: support for wildcard reexports (#1321)
Browse files Browse the repository at this point in the history
* feat(babel): support for wildcard reexports

* feat: ActionQueue now has generalised callbacks for actions

* fix(babel): unhandled promise rejection

* fix: extract explodeReexport as a separete action

* test: a bunch of tests for ActionQueue and related code

* fix(babel): resolve circular imports

* fix(babel): add cache invalidation if an entrypoint was created with a different source code

* fix(babel): abort actions if entrypoint was superseded

* fix(babel): do not run the same action twice

* fix(babel): fix concurrent processing

* fix(babel): do not process already superseded entrypoints

* fix(babel): fix keys collision in PriorityQueue

* fix linter

* fix(babel): resolve an issue with circular imports

* fix(babel): simplified isLoop check

* feat(logs): hide source code under *:source namesapce

* fix(babel): do not add action if it was already processed in a queue

* fix(babel): use multiline regexp for esm detection

* chore: changeset
  • Loading branch information
Anber committed Aug 18, 2023
1 parent ea1444f commit 88e0761
Show file tree
Hide file tree
Showing 57 changed files with 2,824 additions and 931 deletions.
29 changes: 29 additions & 0 deletions .changeset/few-kings-sip.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
---
'@linaria/babel-preset': major
'@linaria/logger': major
'@linaria/rollup': major
'@linaria/testkit': major
'@linaria/utils': major
'@linaria/atomic': major
'@linaria/cli': major
'@linaria/core': major
'@linaria/esbuild': major
'@linaria/extractor': major
'@linaria/griffel': major
'@linaria/babel-plugin-interop': major
'linaria': major
'@linaria/postcss-linaria': major
'@linaria/react': major
'@linaria/server': major
'@linaria/shaker': major
'@linaria/stylelint': major
'@linaria/stylelint-config-standard-linaria': major
'@linaria/tags': major
'@linaria/vite': major
'@linaria/webpack-loader': major
'@linaria/webpack4-loader': major
'@linaria/webpack5-loader': major
'linaria-website': major
---

Rewritten dependecny tree processing with support for wildcard re-exports.
2 changes: 1 addition & 1 deletion docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,7 @@ module.exports = {
return false;
}
return /\b(?:export|import)\b/.test(code);
return /\b(?:export|import)\b/m.test(code);
},
action: require.resolve('@linaria/shaker'),
}
Expand Down
2 changes: 1 addition & 1 deletion examples/vite/.linariarc.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ module.exports = {
return false;
}

return /(?:^|\n|;)\s*(?:export|import)\s+/.test(code);
return /(?:^|\n|;)\s*(?:export|import)\s+/m.test(code);
},
action: require.resolve('@linaria/shaker'),
},
Expand Down
1 change: 1 addition & 0 deletions packages/babel/jest.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,5 @@
module.exports = {
preset: 'ts-jest',
testEnvironment: 'node',
testMatch: ['**/__tests__/**/*.test.ts'],
};
1 change: 1 addition & 0 deletions packages/babel/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@
"@linaria/utils": "workspace:^",
"cosmiconfig": "^8.0.0",
"happy-dom": "10.8.0",
"nanoid": "^3.3.6",
"source-map": "^0.7.3",
"stylis": "^3.5.4"
},
Expand Down
14 changes: 8 additions & 6 deletions packages/babel/src/cache.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,16 +4,14 @@ import type { File } from '@babel/types';

import { linariaLogger } from '@linaria/logger';

import type { IModule } from './module';
import type { IEntrypoint } from './transform-stages/queue/types';
import type { ITransformFileResult } from './types';
import type { IBaseEntrypoint, IModule, ITransformFileResult } from './types';

function hashContent(content: string) {
return createHash('sha256').update(content).digest('hex');
}

interface ICaches {
entrypoints: Map<string, IEntrypoint>;
entrypoints: Map<string, IBaseEntrypoint>;
ignored: Map<string, true>;
resolve: Map<string, string>;
resolveTask: Map<
Expand Down Expand Up @@ -62,7 +60,7 @@ const loggers = cacheNames.reduce(
export class TransformCacheCollection {
private contentHashes = new Map<string, string>();

protected readonly entrypoints: Map<string, IEntrypoint>;
protected readonly entrypoints: Map<string, IBaseEntrypoint>;

protected readonly ignored: Map<string, true>;

Expand Down Expand Up @@ -146,8 +144,12 @@ export class TransformCacheCollection {
}

public invalidate(cacheName: CacheNames, key: string): void {
loggers[cacheName]('invalidate', key);
const cache = this[cacheName] as Map<string, unknown>;
if (!cache.has(key)) {
return;
}

loggers[cacheName]('invalidate', key);

cache.delete(key);
}
Expand Down
4 changes: 1 addition & 3 deletions packages/babel/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,19 +8,17 @@ import type { ConfigAPI, TransformCaller } from '@babel/core';
import { debug } from '@linaria/logger';

import transform from './plugins/babel-transform';
import type { PluginOptions } from './transform-stages/helpers/loadLinariaOptions';
import type { PluginOptions } from './types';

export { slugify } from '@linaria/utils';

export { default as preeval } from './plugins/preeval';
export { default as withLinariaMetadata } from './utils/withLinariaMetadata';
export { default as Module, DefaultModuleImplementation } from './module';
export type { IModule } from './module';
export { default as transform } from './transform';
export * from './types';
export { parseFile } from './transform-stages/helpers/parseFile';
export { default as loadLinariaOptions } from './transform-stages/helpers/loadLinariaOptions';
export type { PluginOptions } from './transform-stages/helpers/loadLinariaOptions';
export { prepareCode } from './transform-stages/queue/actions/transform';
export { createEntrypoint } from './transform-stages/queue/createEntrypoint';
export { transformUrl } from './transform-stages/4-extract';
Expand Down
19 changes: 7 additions & 12 deletions packages/babel/src/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ import type { StrictOptions } from '@linaria/utils';
import { getFileIdx } from '@linaria/utils';

import { TransformCacheCollection } from './cache';
import type { IModule } from './types';
import createVmContext from './vm/createVmContext';

type HiddenModuleMembers = {
Expand Down Expand Up @@ -94,14 +95,6 @@ const hasKey = <TKey extends string | symbol>(
obj !== null &&
key in obj;

export interface IModule {
debug: CustomDebug;
readonly exports: unknown;
readonly idx: number;
readonly isEvaluated: boolean;
readonly only: string;
}

function getUncached(cached: string | string[], test: string): string[] {
if (cached === test) {
return [];
Expand Down Expand Up @@ -334,8 +327,9 @@ class Module {
if (uncachedExports.length > 0 || !m.isEvaluated) {
m.debug(
'eval-cache',
'is going to be invalidated because %o is not evaluated yet',
uncachedExports
'is going to be invalidated because %o is not evaluated yet (evaluated: %o)',
uncachedExports,
m.only
);

this.cache.invalidate('eval', filename);
Expand Down Expand Up @@ -379,8 +373,9 @@ class Module {

log(
'code-cache',
'❌ file has been processed during prepare stage but %o is not evaluated yet',
uncachedExports
'❌ file has been processed during prepare stage but %o is not evaluated yet (evaluated: %o)',
uncachedExports,
cached?.only ?? []
);
} else {
log('code-cache', '❌ file has not been processed during prepare stage');
Expand Down
3 changes: 1 addition & 2 deletions packages/babel/src/plugins/babel-transform.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,8 @@ import type { Core } from '../babel';
import { TransformCacheCollection } from '../cache';
import { prepareForEvalSync } from '../transform-stages/1-prepare-for-eval';
import evalStage from '../transform-stages/2-eval';
import type { PluginOptions } from '../transform-stages/helpers/loadLinariaOptions';
import loadLinariaOptions from '../transform-stages/helpers/loadLinariaOptions';
import type { IPluginState } from '../types';
import type { IPluginState, PluginOptions } from '../types';
import { processTemplateExpression } from '../utils/processTemplateExpression';
import withLinariaMetadata from '../utils/withLinariaMetadata';

Expand Down
50 changes: 26 additions & 24 deletions packages/babel/src/plugins/preeval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,36 +41,38 @@ export default function preeval(
const rootScope = file.scope;
this.processors = [];

const onProcessTemplateFinished = eventEmitter.pair({
method: 'queue:transform:preeval:processTemplate',
});

file.path.traverse({
Identifier: (p) => {
processTemplateExpression(p, file.opts, options, (processor) => {
processor.dependencies.forEach((dependency) => {
if (dependency.ex.type === 'Identifier') {
addIdentifierToLinariaPreval(rootScope, dependency.ex.name);
}
});

processor.doEvaltimeReplacement();
this.processors.push(processor);
});
eventEmitter.pair(
{
method: 'queue:transform:preeval:processTemplate',
},
});

onProcessTemplateFinished();
() => {
file.path.traverse({
Identifier: (p) => {
processTemplateExpression(p, file.opts, options, (processor) => {
processor.dependencies.forEach((dependency) => {
if (dependency.ex.type === 'Identifier') {
addIdentifierToLinariaPreval(rootScope, dependency.ex.name);
}
});

processor.doEvaltimeReplacement();
this.processors.push(processor);
});
},
});
}
);

if (
isFeatureEnabled(options.features, 'dangerousCodeRemover', filename)
) {
log('start', 'Strip all JSX and browser related stuff');
const onCodeRemovingFinished = eventEmitter.pair({
method: 'queue:transform:preeval:removeDangerousCode',
});
removeDangerousCode(file.path);
onCodeRemovingFinished();
eventEmitter.pair(
{
method: 'queue:transform:preeval:removeDangerousCode',
},
() => removeDangerousCode(file.path)
);
}

onFinishCallbacks.set(
Expand Down
34 changes: 19 additions & 15 deletions packages/babel/src/transform-stages/1-prepare-for-eval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,10 @@ import type { Core } from '../babel';
import type { TransformCacheCollection } from '../cache';
import type { ITransformFileResult, Options } from '../types';

import { AsyncActionQueue, SyncActionQueue } from './helpers/ActionQueue';
import { AsyncActionQueue, SyncActionQueue } from './queue/ActionQueue';
import { addToCodeCache } from './queue/actions/addToCodeCache';
import { explodeReexports } from './queue/actions/explodeReexports';
import { getExports } from './queue/actions/getExports';
import { processEntrypoint } from './queue/actions/processEntrypoint';
import { processImports } from './queue/actions/processImports';
import {
Expand All @@ -27,26 +29,27 @@ export function prepareForEvalSync(
options: Pick<Options, 'root' | 'inputSourceMap'>,
eventEmitter = EventEmitter.dummy
): ITransformFileResult | undefined {
const services = { babel, cache, options, eventEmitter };

const entrypoint = createEntrypoint(
babel,
rootLog,
cache,
services,
{ log: rootLog },
partialEntrypoint.name,
partialEntrypoint.only,
partialEntrypoint.code,
pluginOptions,
options,
eventEmitter
pluginOptions
);

if (entrypoint === 'ignored') {
return undefined;
}

const queue = new SyncActionQueue(
{ babel, cache, options, eventEmitter },
services,
{
addToCodeCache,
explodeReexports,
getExports,
processEntrypoint,
processImports,
resolveImports: syncResolveImports.bind(null, resolve),
Expand Down Expand Up @@ -79,6 +82,8 @@ export default async function prepareForEval(
options: Pick<Options, 'root' | 'inputSourceMap'>,
eventEmitter = EventEmitter.dummy
): Promise<ITransformFileResult | undefined> {
const services = { babel, cache, options, eventEmitter };

/*
* This method can be run simultaneously for multiple files.
* A shared cache is accessible for all runs, but each run has its own queue
Expand All @@ -88,25 +93,24 @@ export default async function prepareForEval(
* the combined "only" option.
*/
const entrypoint = createEntrypoint(
babel,
rootLog,
cache,
services,
{ log: rootLog },
partialEntrypoint.name,
partialEntrypoint.only,
partialEntrypoint.code,
pluginOptions,
options,
eventEmitter
pluginOptions
);

if (entrypoint === 'ignored') {
return undefined;
}

const queue = new AsyncActionQueue(
{ babel, cache, options, eventEmitter },
services,
{
addToCodeCache,
explodeReexports,
getExports,
processEntrypoint,
processImports,
resolveImports: asyncResolveImports.bind(null, resolve),
Expand Down

0 comments on commit 88e0761

Please sign in to comment.