Skip to content

Commit

Permalink
allow Locator#load to be called concurrently
Browse files Browse the repository at this point in the history
- Until now, Locator could recursively load multiple css files.
- However, with the change in the loader interface, this no longer happens.
- We now allow concurrent calls to `Locator#load`.
  • Loading branch information
mizdra committed Jun 9, 2024
1 parent 865c18b commit 2d63a9b
Show file tree
Hide file tree
Showing 3 changed files with 1 addition and 64 deletions.
54 changes: 0 additions & 54 deletions packages/happy-css-modules/src/locator/index.test.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,7 @@
import { readFile, writeFile } from 'fs/promises';
import { randomUUID } from 'node:crypto';
import { jest } from '@jest/globals';
import dedent from 'dedent';
import { Locator, createDefaultTransformer } from '../index.js';
import { createFixtures, getFixturePath } from '../test-util/util.js';
import { sleepSync } from '../util.js';

const locator = new Locator();

Expand Down Expand Up @@ -159,48 +156,6 @@ test('normalizes tokens', async () => {
`);
});

test('returns the result from the cache when the file has not been modified', async () => {
createFixtures({
'/test/1.css': dedent`
@import './2.css';
@import './3.css';
`,
'/test/2.css': dedent`
.b {}
`,
'/test/3.css': dedent`
.c {}
.d {}
`,
});
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const readCSSSpy = jest.spyOn(locator, 'readCSS' as any);
await locator.load(getFixturePath('/test/1.css'));
expect(readCSSSpy).toHaveBeenCalledTimes(3);
expect(readCSSSpy).toHaveBeenNthCalledWith(1, getFixturePath('/test/1.css'));
expect(readCSSSpy).toHaveBeenNthCalledWith(2, getFixturePath('/test/2.css'));
expect(readCSSSpy).toHaveBeenNthCalledWith(3, getFixturePath('/test/3.css'));
readCSSSpy.mockClear();

// update `/test/2.css`
sleepSync(1); // wait for the file system to update the mtime
await writeFile(getFixturePath('/test/2.css'), await readFile(getFixturePath('/test/2.css'), 'utf-8'));

// `3.css` is not updated, so the cache is used. Therefore, `readFile` is not called.
await locator.load(getFixturePath('/test/3.css'));
expect(readCSSSpy).toHaveBeenCalledTimes(0);

// `1.css` is not updated, but dependencies are updated, so the cache is used. Therefore, `readFile` is called.
await locator.load(getFixturePath('/test/1.css'));
expect(readCSSSpy).toHaveBeenCalledTimes(2);
expect(readCSSSpy).toHaveBeenNthCalledWith(1, getFixturePath('/test/1.css'));
expect(readCSSSpy).toHaveBeenNthCalledWith(2, getFixturePath('/test/2.css'));

// ``2.css` is updated, but the cache is already available because it was updated in the previous step. Therefore, `readFile` is not called.
await locator.load(getFixturePath('/test/2.css'));
expect(readCSSSpy).toHaveBeenCalledTimes(2);
});

describe('supports sourcemap', () => {
test('restores original locations from sourcemap', async () => {
const transformer = createDefaultTransformer();
Expand Down Expand Up @@ -309,12 +264,3 @@ test('ignores http(s) protocol file', async () => {
const result = await locator.load(getFixturePath('/test/1.css'));
expect(result).toStrictEqual({ tokenInfos: [], transpileDependencies: [] });
});

test('block concurrent calls to load method', async () => {
createFixtures({
'/test/1.css': `.a {}`,
});
await expect(async () => {
await Promise.all([locator.load(getFixturePath('/test/1.css')), locator.load(getFixturePath('/test/1.css'))]);
}).rejects.toThrowError('Cannot call `Locator#load` concurrently.');
});
10 changes: 0 additions & 10 deletions packages/happy-css-modules/src/locator/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,6 @@ export class Locator {
private readonly cache: Map<string, CacheEntry> = new Map();
private readonly transformer: Transformer | undefined;
private readonly resolver: StrictlyResolver;
private loading = false;

constructor(options?: LocatorOptions) {
this.transformer = options?.transformer ?? createDefaultTransformer();
Expand Down Expand Up @@ -114,15 +113,6 @@ export class Locator {

/** Returns information about the tokens exported from the CSS Modules file. */
async load(filePath: string): Promise<LoadResult> {
if (this.loading) throw new Error('Cannot call `Locator#load` concurrently.');
this.loading = true;
const result = await this._load(filePath).finally(() => {
this.loading = false;
});
return result;
}

private async _load(filePath: string): Promise<LoadResult> {
const mtime = (await stat(filePath)).mtime.getTime();

const { css, map, dependencies: transpileDependencies } = await this.readCSS(filePath);
Expand Down
1 change: 1 addition & 0 deletions packages/happy-css-modules/src/runner.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,7 @@ export async function run(options: RunnerOptions): Promise<Watcher | void> {
}

// Locator#load cannot be called concurrently. Therefore, it takes a lock and waits.
// TODO: Concurrent calls to Locator#load are now allowed. Therefore, it is necessary to remove the lock.
await lock.acquireAsync();

try {
Expand Down

0 comments on commit 2d63a9b

Please sign in to comment.