Skip to content

Commit

Permalink
fix(webpack): add cacheType to support both memory and filesystem cac…
Browse files Browse the repository at this point in the history
…he (#884, fixes #881)
  • Loading branch information
malash committed Dec 17, 2021
1 parent 03312a7 commit 1e39317
Show file tree
Hide file tree
Showing 4 changed files with 96 additions and 33 deletions.
35 changes: 19 additions & 16 deletions docs/BUNDLERS_INTEGRATION.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
# Bundlers Integration

## Jump To

- [webpack](#webpack)
- [Rollup](#Rollup)
- [Svelte](#Svelte)
Expand All @@ -11,7 +12,7 @@ If you use Babel in your project, make sure to have a [config file for Babel](ht

## Bundlers

Please note, that `@babel/core` is a peer dependency of all loaders. Do not forget to add it to `devDependencies` list in your project.
Please note, that `@babel/core` is a peer dependency of all loaders. Do not forget to add it to `devDependencies` list in your project.

### webpack

Expand Down Expand Up @@ -182,15 +183,16 @@ The loader accepts the following options:

Setting this option to `true` will include source maps for the generated CSS so that you can see where source of the class name in devtools. We recommend to enable this only in development mode because the sourcemap is inlined into the CSS files.

- `cacheDirectory: string` (default: `'.linaria-cache'`):

Path to the directory where the loader will output the intermediate CSS files. You can pass a relative or absolute directory path. Make sure the directory is inside the working directory for things to work properly. **You should add this directory to `.gitignore` so you don't accidentally commit them.**

- `extension: string` (default: `'.linaria.css'`):

An extension of the intermediate CSS files.
- `cacheProvider: undefined | string | ICache` (default: `undefined`):
By default Linaria use a memory cache to store temporary CSS files. But if you are using this loader with [thread-loader](https://www.npmjs.com/package/thread-loader) you should use some consistent cache to prevent [some unexpected issues](https://github.com/callstack/linaria/issues/881). This options support a `ICache` instance or a path to NodeJS module which export a `ICache` instance as `module.exports`
> ```
> interface ICache {
> get: (key: string) => Promise<string>;
> set: (key: string, value: string) => Promise<void>
> }
> ```
- `preprocessor: 'none' | 'stylis' | Function` (default: `'stylis'`)
* `preprocessor: 'none' | 'stylis' | Function` (default: `'stylis'`)

You can override the pre-processor if you want to override how the loader processes the CSS.

Expand Down Expand Up @@ -256,32 +258,34 @@ export default {
};
```


If you are using [@rollup/plugin-babel](https://github.com/rollup/plugins/tree/master/packages/babel) as well, ensure the linaria plugin is declared earlier in the `plugins` array than your babel plugin.
If you are using [@rollup/plugin-babel](https://github.com/rollup/plugins/tree/master/packages/babel) as well, ensure the linaria plugin is declared earlier in the `plugins` array than your babel plugin.

```js
import linaria from 'linaria/rollup';
import css from 'rollup-plugin-css-only';
import babel from "@rollup/plugin-babel";
import babel from '@rollup/plugin-babel';

export default {
/* rest of your config */
/* rest of your config */
plugins: [
linaria({
sourceMap: process.env.NODE_ENV !== 'production',
}),
css({
output: 'styles.css',
}),
babel({/**/}),
/* rest of your plugins */
babel({
/**/
}),
/* rest of your plugins */
],
};
```

### Svelte

#### Contents

- [Svelte with Rollup](#Rollup-1)
- [Svelte with Webpack](#Webpack-1)

Expand Down Expand Up @@ -362,4 +366,3 @@ module.exports = {
},
};
```

45 changes: 45 additions & 0 deletions src/cache.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
export interface ICache {
get: (key: string) => Promise<string>;
set: (key: string, value: string) => Promise<void>;
}

// memory cache, which is the default cache implementation in Linaria

class MemoryCache implements ICache {
private cache: Map<string, string> = new Map();

public get(key: string): Promise<string> {
return Promise.resolve(this.cache.get(key) ?? '');
}

public set(key: string, value: string): Promise<void> {
this.cache.set(key, value);
return Promise.resolve();
}
}

export const memoryCache = new MemoryCache();

/**
* return cache instance from `options.cacheProvider`
* @param cacheProvider string | ICache | undefined
* @returns ICache instance
*/
export const getCacheInstance = async (
cacheProvider: string | ICache | undefined
): Promise<ICache> => {
if (!cacheProvider) {
return memoryCache;
}
if (typeof cacheProvider === 'string') {
return require(cacheProvider);
}
if (
typeof cacheProvider === 'object' &&
'get' in cacheProvider &&
'set' in cacheProvider
) {
return cacheProvider;
}
throw new Error(`Invalid cache provider: ${cacheProvider}`);
};
33 changes: 22 additions & 11 deletions src/loader.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ import * as EvalCache from './babel/eval-cache';
import Module from './babel/module';
import { debug } from './babel/utils/logger';
import transform from './transform';
import { addFile } from './outputCssLoader';
import { getCacheInstance } from './cache';
import type { Result } from './types';

type LoaderContext = Parameters<typeof loaderUtils.getOptions>[0];

Expand All @@ -23,6 +24,9 @@ export default function loader(
content: string,
inputSourceMap: RawSourceMap | null
) {
// tell Webpack this loader is async
this.async();

debug('loader', this.resourcePath);

EvalCache.clearForFile(this.resourcePath);
Expand All @@ -31,6 +35,7 @@ export default function loader(
sourceMap = undefined,
preprocessor = undefined,
extension = '.linaria.css',
cacheProvider,
...rest
} = loaderUtils.getOptions(this) || {};

Expand All @@ -55,7 +60,7 @@ export default function loader(
: resolveOptions
);

let result;
let result: Result;

const originalResolveFilename = Module._resolveFilename;

Expand Down Expand Up @@ -100,15 +105,21 @@ export default function loader(
});
}

addFile(this.resourcePath, cssText);
const request = `${outputFilename}!=!${outputCssLoader}!${this.resourcePath}`;
const stringifiedRequest = loaderUtils.stringifyRequest(this, request);

this.callback(
null,
`${result.code}\n\nrequire(${stringifiedRequest});`,
result.sourceMap ?? undefined
);
getCacheInstance(cacheProvider)
.then((cacheInstance) => cacheInstance.set(this.resourcePath, cssText))
.then(() => {
const request = `${outputFilename}!=!${outputCssLoader}?cacheProvider=${encodeURIComponent(
cacheProvider
)}!${this.resourcePath}`;
const stringifiedRequest = loaderUtils.stringifyRequest(this, request);

return this.callback(
null,
`${result.code}\n\nrequire(${stringifiedRequest});`,
result.sourceMap ?? undefined
);
})
.catch((err: Error) => this.callback(err));
return;
}

Expand Down
16 changes: 10 additions & 6 deletions src/outputCssLoader.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
const cssLookup = new Map<string, string>();
import loaderUtils from 'loader-utils';
import { getCacheInstance } from './cache';

export const addFile = (id: string, content: string) => {
cssLookup.set(id, content);
};
type LoaderContext = Parameters<typeof loaderUtils.getOptions>[0];

export default function outputCssLoader(this: { resourcePath: string }) {
return cssLookup.get(this.resourcePath) ?? '';
export default function outputCssLoader(this: LoaderContext) {
this.async();
const { cacheProvider } = loaderUtils.getOptions(this) || {};
getCacheInstance(cacheProvider)
.then((cacheInstance) => cacheInstance.get(this.resourcePath))
.then((result) => this.callback(null, result))
.catch((err: Error) => this.callback(err));
}

0 comments on commit 1e39317

Please sign in to comment.