Skip to content

Commit

Permalink
feat(resolver): add custom resolver option to support re-exporting of…
Browse files Browse the repository at this point in the history
… linaria libs (#882)

* feat(resolver): add custom linariaLibResolver

* docs(resolver): add tests and docs for libResolver
  • Loading branch information
jpnelson authored Dec 9, 2021
1 parent bc3cc26 commit ad4a368
Show file tree
Hide file tree
Showing 10 changed files with 131 additions and 21 deletions.
39 changes: 39 additions & 0 deletions docs/CONFIGURATION.md
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,45 @@ module.exports = {

To configure for use with `@linaria/atomic`, set this option to `atomize: require('@linaria/atomic').atomize`

- `libResolver: (source) => string`

A custom function to use when resolving linaria libraries during babel compilation.

By default, linaria APIs like `css` and `styled` **must** be imported directly from the package – this is because babel needs to be able to recognize the API's to do static style extraction. `libResolver` allows `css` and `styled` APIs to be imported from other files too.

`libResolver` takes the path for the source module (eg. `@linaria/core`), and returns the full file path to resolve this module to.

For example, we can use this to map `@linaria/core` , `@linaria/react` or `@linaria/atomic` where we re-export the module.

```js
{
libResolver: (source) => {
if (source === '@linaria/core') {
return require.resolve('./my-local-folder/core');
} else if (source === '@linaria/react') {
return require.resolve('./my-local-folder/react');
}
return null;
};
}
```

We can then re-export and use linaria API's from `./my-local-folder`:

```js
// my-file.js
import { css } from './my-local-folder/core';

export default css`
border: 1px solid black;
`;
```

```js
// ./my-local-folder/core.js
export * from '@linaria/core';
```

- `babelOptions: Object`

If you need to specify custom babel configuration, you can pass them here. These babel options will be used by Linaria when parsing and evaluating modules.
Expand Down
19 changes: 19 additions & 0 deletions packages/babel/__tests__/__snapshots__/babel.test.ts.snap
Original file line number Diff line number Diff line change
@@ -1,5 +1,24 @@
// Jest Snapshot v1, https://goo.gl/fbAQLP

exports[`can re-export lib apis using a custom resolver 1`] = `
"import { css } from './my-folder';
const x = \\"x17gu1mi\\";
console.log(x);"
`;

exports[`can re-export lib apis using a custom resolver 2`] = `
CSS:
.x17gu1mi {
background: red;
height: 100px;
}
Dependencies: NA
`;

exports[`compiles atomic css 1`] = `
"/* @flow */
import { css } from '@linaria/atomic';
Expand Down
27 changes: 27 additions & 0 deletions packages/babel/__tests__/babel.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -578,3 +578,30 @@ it('compiles atomic css', async () => {
expect(code).toMatchSnapshot();
expect(metadata).toMatchSnapshot();
});

it('can re-export lib apis using a custom resolver', async () => {
const { code, metadata } = await transpile(
dedent`
import { css } from './my-folder';
const x = css\`
background: red;
height: 100px;
\`;
console.log(x);
`,
{
libResolver: () => {
// In testing, since ./my-folder isn't a real folder, to match the
// resolution we need to return null here. In a real use case, the full
// file path to the correct resolution would be returned
return null;
},
}
);

expect(code).toMatchSnapshot();
expect(metadata).toMatchSnapshot();
});
3 changes: 3 additions & 0 deletions packages/babel/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,8 @@ type AtomizeFn = (cssText: string) => {
property: string;
}[];

export type LibResolverFn = (linariaLibPath: string) => string | null;

export type StrictOptions = {
classNameSlug?: string | ClassNameFn;
displayName: boolean;
Expand All @@ -143,6 +145,7 @@ export type StrictOptions = {
atomize?: AtomizeFn;
babelOptions: TransformOptions;
rules: EvalRule[];
libResolver?: LibResolverFn;
};

export type Location = {
Expand Down
52 changes: 35 additions & 17 deletions packages/babel/src/utils/getTemplateType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ import type {
TaggedTemplateExpression,
} from '@babel/types';
import type { NodePath } from '@babel/traverse';
import type { State, TemplateExpression } from '../types';
import type { State, TemplateExpression, LibResolverFn } from '../types';
import { Core } from '../babel';
import hasImport from './hasImport';

Expand All @@ -19,7 +19,8 @@ const cache = new WeakMap<NodePath<TaggedTemplateExpression>, Result>();
export default function getTemplateType(
{ types: t }: Core,
path: NodePath<TaggedTemplateExpression>,
state: State
state: State,
libResolver?: LibResolverFn
): Result {
if (!cache.has(path)) {
const { tag } = path.node;
Expand All @@ -31,10 +32,14 @@ export default function getTemplateType(
t.isIdentifier(tag.callee) &&
tag.arguments.length === 1 &&
tag.callee.name === localName &&
hasImport(t, path.scope, state.file.opts.filename, localName, [
'@linaria/react',
'linaria/react',
])
hasImport(
t,
path.scope,
state.file.opts.filename,
localName,
['@linaria/react', 'linaria/react'],
libResolver
)
) {
const tagPath = path.get('tag') as NodePath<CallExpression>;
cache.set(path, {
Expand All @@ -45,27 +50,40 @@ export default function getTemplateType(
t.isIdentifier(tag.object) &&
t.isIdentifier(tag.property) &&
tag.object.name === localName &&
hasImport(t, path.scope, state.file.opts.filename, localName, [
'@linaria/react',
'linaria/react',
])
hasImport(
t,
path.scope,
state.file.opts.filename,
localName,
['@linaria/react', 'linaria/react'],
libResolver
)
) {
cache.set(path, {
component: { node: t.stringLiteral(tag.property.name) },
});
} else if (
hasImport(t, path.scope, state.file.opts.filename, 'css', [
'@linaria/core',
'linaria',
]) &&
hasImport(
t,
path.scope,
state.file.opts.filename,
'css',
['@linaria/core', 'linaria'],
libResolver
) &&
t.isIdentifier(tag) &&
tag.name === 'css'
) {
cache.set(path, 'css');
} else if (
hasImport(t, path.scope, state.file.opts.filename, 'css', [
'@linaria/atomic',
]) &&
hasImport(
t,
path.scope,
state.file.opts.filename,
'css',
['@linaria/atomic'],
libResolver
) &&
t.isIdentifier(tag) &&
tag.name === 'css'
) {
Expand Down
5 changes: 3 additions & 2 deletions packages/babel/src/utils/hasImport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,8 @@ export default function hasImport(
scope: any,
filename: string,
identifier: string,
sources: string[]
sources: string[],
libResolver = safeResolve
): boolean {
const binding = scope.getAllBindings()[identifier];

Expand Down Expand Up @@ -53,7 +54,7 @@ export default function hasImport(
// Otherwise try to resolve both and check if they are the same file
resolveFromFile(value) ===
(linariaLibs.has(source)
? safeResolve(source)
? libResolver(source)
: resolveFromFile(source))
);

Expand Down
3 changes: 2 additions & 1 deletion packages/babel/src/visitors/CollectDependencies.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,8 +62,9 @@ export default function CollectDependencies(
state: State,
options: StrictOptions
) {
const { libResolver } = options;
const { types: t } = babel;
const templateType = getTemplateType(babel, path, state);
const templateType = getTemplateType(babel, path, state, libResolver);
if (!templateType) {
return;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/babel/src/visitors/GenerateClassNames.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,8 +23,9 @@ export default function GenerateClassNames(
state: State,
options: StrictOptions
) {
const { libResolver } = options;
const { types: t } = babel;
const templateType = getTemplateType(babel, path, state);
const templateType = getTemplateType(babel, path, state, libResolver);
if (!templateType) {
return;
}
Expand Down
Empty file added website/src/api/index.js
Empty file.
1 change: 1 addition & 0 deletions website/src/api/react/index.js
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from '@linaria/react';

0 comments on commit ad4a368

Please sign in to comment.