Compare commits
2 Commits
38408f774e
...
5bf3d6b769
| Author | SHA1 | Date |
|---|---|---|
|
|
5bf3d6b769 | |
|
|
6b489ede0e |
|
|
@ -1,6 +1,6 @@
|
|||
import { readFileSync, existsSync } from 'node:fs';
|
||||
import { dirname, resolve, relative } from 'node:path';
|
||||
import { glob } from 'fast-glob';
|
||||
import fg from 'fast-glob';
|
||||
import type { YarnProject } from './types';
|
||||
import { validateYarnProject, type SchemaValidationError } from './validator';
|
||||
import { parseYarn } from '../yarn-spinner/parse/parser';
|
||||
|
|
@ -104,7 +104,7 @@ export async function loadYarnProject(
|
|||
const sourcePatterns = project.sourceFiles;
|
||||
const ignorePatterns = project.excludeFiles || [];
|
||||
|
||||
const yarnFilePaths = await glob(sourcePatterns, {
|
||||
const yarnFilePaths = await fg.glob(sourcePatterns, {
|
||||
cwd: baseDir,
|
||||
ignore: ignorePatterns,
|
||||
absolute: true,
|
||||
|
|
@ -171,7 +171,7 @@ export function loadYarnProjectSync(
|
|||
const sourcePatterns = project.sourceFiles;
|
||||
const ignorePatterns = project.excludeFiles || [];
|
||||
|
||||
const yarnFilePaths = glob.sync(sourcePatterns, {
|
||||
const yarnFilePaths = fg.globSync(sourcePatterns, {
|
||||
cwd: baseDir,
|
||||
ignore: ignorePatterns,
|
||||
absolute: true,
|
||||
|
|
|
|||
|
|
@ -0,0 +1,150 @@
|
|||
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
||||
import { yarnSpinnerPlugin } from '../../src/plugins/esbuild';
|
||||
import { resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
describe('yarnSpinnerPlugin (esbuild)', () => {
|
||||
// Fixtures are in tests/fixtures/, not tests/plugins/fixtures/
|
||||
const projectPath = resolve(__dirname, '../fixtures/simple/.yarnproject');
|
||||
|
||||
it('should return an esbuild plugin with correct name', () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
expect(plugin.name).toBe('yarn-spinner-loader');
|
||||
});
|
||||
|
||||
it('should have a setup function', () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
expect(plugin.setup).toBeDefined();
|
||||
expect(typeof plugin.setup).toBe('function');
|
||||
});
|
||||
|
||||
it('should register onLoad handler for .yarnproject files', () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
|
||||
const mockBuild = {
|
||||
onLoad: vi.fn((_options, callback) => {
|
||||
// Store the callback for testing
|
||||
(mockBuild as any)._onLoadCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
plugin.setup(mockBuild as any);
|
||||
|
||||
expect(mockBuild.onLoad).toHaveBeenCalledWith(
|
||||
{ filter: /\.yarnproject$/ },
|
||||
expect.any(Function),
|
||||
);
|
||||
});
|
||||
|
||||
it('should load .yarnproject files and return correct content', async () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
|
||||
let onLoadCallback: Function | undefined;
|
||||
const mockBuild = {
|
||||
onLoad: vi.fn((_options, callback) => {
|
||||
onLoadCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
plugin.setup(mockBuild as any);
|
||||
|
||||
// Ensure callback was registered
|
||||
expect(onLoadCallback).toBeDefined();
|
||||
|
||||
// Simulate esbuild calling the callback
|
||||
const args = { path: projectPath };
|
||||
const result = await (onLoadCallback as any)(args);
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(result.contents).toContain('export default');
|
||||
expect(result.contents).toContain('project');
|
||||
expect(result.contents).toContain('yarnFiles');
|
||||
expect(result.loader).toBe('js');
|
||||
});
|
||||
|
||||
it('should handle errors by returning error object', async () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
|
||||
let onLoadCallback: Function | undefined;
|
||||
const mockBuild = {
|
||||
onLoad: vi.fn((_options, callback) => {
|
||||
onLoadCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
plugin.setup(mockBuild as any);
|
||||
|
||||
// Simulate esbuild calling the callback with non-existent file
|
||||
const args = { path: 'nonexistent/.yarnproject' };
|
||||
const result = await (onLoadCallback as any)(args);
|
||||
|
||||
expect(result.errors).toBeDefined();
|
||||
expect(result.errors.length).toBeGreaterThan(0);
|
||||
expect(result.errors[0].text).toBeDefined();
|
||||
expect(typeof result.errors[0].text).toBe('string');
|
||||
});
|
||||
|
||||
it('should accept loadOptions and pass them to loadYarnProject', async () => {
|
||||
const plugin = yarnSpinnerPlugin({
|
||||
loadOptions: {
|
||||
baseDir: resolve(__dirname, 'fixtures/simple'),
|
||||
},
|
||||
});
|
||||
|
||||
let onLoadCallback: Function | undefined;
|
||||
const mockBuild = {
|
||||
onLoad: vi.fn((_options, callback) => {
|
||||
onLoadCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
plugin.setup(mockBuild as any);
|
||||
|
||||
const args = { path: projectPath };
|
||||
const result = await (onLoadCallback as any)(args);
|
||||
|
||||
expect(result.contents).toContain('export default');
|
||||
expect(result.loader).toBe('js');
|
||||
});
|
||||
|
||||
it('should correctly serialize the result as JSON with proper formatting', async () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
|
||||
let onLoadCallback: Function | undefined;
|
||||
const mockBuild = {
|
||||
onLoad: vi.fn((_options, callback) => {
|
||||
onLoadCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
plugin.setup(mockBuild as any);
|
||||
|
||||
const args = { path: projectPath };
|
||||
const result = await (onLoadCallback as any)(args);
|
||||
|
||||
// The contents should be properly formatted JavaScript code
|
||||
expect(result.contents).toMatch(/export default {[\s\S]*};/);
|
||||
expect(result.loader).toBe('js');
|
||||
});
|
||||
|
||||
it('should handle non-Error objects in error handling', async () => {
|
||||
const plugin = yarnSpinnerPlugin();
|
||||
|
||||
let onLoadCallback: Function | undefined;
|
||||
const mockBuild = {
|
||||
onLoad: vi.fn((_options, callback) => {
|
||||
onLoadCallback = callback;
|
||||
}),
|
||||
};
|
||||
|
||||
plugin.setup(mockBuild as any);
|
||||
|
||||
const args = { path: 'nonexistent/.yarnproject' };
|
||||
const result = await (onLoadCallback as any)(args);
|
||||
|
||||
expect(result.errors[0].text).toBeDefined();
|
||||
expect(typeof result.errors[0].text).toBe('string');
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,104 @@
|
|||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { yarnSpinnerRollup } from '../../src/plugins/rollup';
|
||||
import { resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
describe('yarnSpinnerRollup', () => {
|
||||
// Fixtures are in tests/fixtures/, not tests/plugins/fixtures/
|
||||
const projectPath = resolve(__dirname, '../fixtures/simple/.yarnproject');
|
||||
|
||||
it('should return a Rollup plugin with correct name', () => {
|
||||
const plugin = yarnSpinnerRollup();
|
||||
expect(plugin.name).toBe('yarn-spinner-loader');
|
||||
});
|
||||
|
||||
it('should have a load function', () => {
|
||||
const plugin = yarnSpinnerRollup();
|
||||
expect(plugin.load).toBeDefined();
|
||||
expect(typeof plugin.load).toBe('function');
|
||||
});
|
||||
|
||||
it('should load .yarnproject files and return exported code', async () => {
|
||||
const plugin = yarnSpinnerRollup();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockContext = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const result = await loadFn.call(mockContext, projectPath);
|
||||
|
||||
// If error was called, the file wasn't found or was invalid
|
||||
if (mockContext.error.mock.calls.length > 0) {
|
||||
throw new Error(mockContext.error.mock.calls[0][0]);
|
||||
}
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toContain('export default');
|
||||
expect(result).toContain('project');
|
||||
expect(result).toContain('yarnFiles');
|
||||
});
|
||||
|
||||
it('should return null for non-.yarnproject files', async () => {
|
||||
const plugin = yarnSpinnerRollup();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const result = await loadFn.call({}, 'some-file.ts');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle errors by calling this.error', async () => {
|
||||
const plugin = yarnSpinnerRollup();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockThis = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
await loadFn.call(mockThis, 'nonexistent/.yarnproject');
|
||||
|
||||
expect(mockThis.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should accept loadOptions and pass them to loadYarnProject', async () => {
|
||||
const plugin = yarnSpinnerRollup({
|
||||
loadOptions: {
|
||||
baseDir: resolve(__dirname, '../fixtures/simple'),
|
||||
},
|
||||
});
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockContext = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const result = await loadFn.call(mockContext, projectPath);
|
||||
|
||||
expect(mockContext.error).not.toHaveBeenCalled();
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toContain('export default');
|
||||
});
|
||||
|
||||
it('should correctly serialize the result as JSON', async () => {
|
||||
const plugin = yarnSpinnerRollup();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockContext = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const result = await loadFn.call(mockContext, projectPath);
|
||||
|
||||
if (mockContext.error.mock.calls.length > 0) {
|
||||
throw new Error(mockContext.error.mock.calls[0][0]);
|
||||
}
|
||||
|
||||
// The result should be valid JavaScript code
|
||||
expect(result).toMatch(/export default {[\s\S]*};/);
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,113 @@
|
|||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { yarnSpinnerVite } from '../../src/plugins/vite';
|
||||
import { resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
describe('yarnSpinnerVite', () => {
|
||||
// Fixtures are in tests/fixtures/, not tests/plugins/fixtures/
|
||||
const projectPath = resolve(__dirname, '../fixtures/simple/.yarnproject');
|
||||
|
||||
it('should return a Vite plugin with correct name', () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
expect(plugin.name).toBe('yarn-spinner-loader');
|
||||
});
|
||||
|
||||
it('should have a load function', () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
expect(plugin.load).toBeDefined();
|
||||
expect(typeof plugin.load).toBe('function');
|
||||
});
|
||||
|
||||
it('should load .yarnproject files and return exported code', async () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockContext = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const result = await loadFn.call(mockContext, projectPath);
|
||||
|
||||
// If error was called, the file wasn't found or was invalid
|
||||
if (mockContext.error.mock.calls.length > 0) {
|
||||
throw new Error(mockContext.error.mock.calls[0][0]);
|
||||
}
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toContain('export default');
|
||||
expect(result).toContain('project');
|
||||
expect(result).toContain('yarnFiles');
|
||||
});
|
||||
|
||||
it('should return null for non-.yarnproject files', async () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const result = await loadFn.call({}, 'some-file.ts');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
|
||||
it('should handle errors by calling this.error', async () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockThis = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
await loadFn.call(mockThis, 'nonexistent/.yarnproject');
|
||||
|
||||
expect(mockThis.error).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should accept loadOptions and pass them to loadYarnProject', async () => {
|
||||
const plugin = yarnSpinnerVite({
|
||||
loadOptions: {
|
||||
baseDir: resolve(__dirname, '../fixtures/simple'),
|
||||
},
|
||||
});
|
||||
const loadFn = plugin.load as Function;
|
||||
|
||||
const mockContext = {
|
||||
error: vi.fn(),
|
||||
};
|
||||
|
||||
const result = await loadFn.call(mockContext, projectPath);
|
||||
|
||||
expect(mockContext.error).not.toHaveBeenCalled();
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toContain('export default');
|
||||
});
|
||||
|
||||
it('should have a transform function', () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
expect(plugin.transform).toBeDefined();
|
||||
expect(typeof plugin.transform).toBe('function');
|
||||
});
|
||||
|
||||
it('transform should return empty export for .yarnproject files', () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
const transformFn = plugin.transform as Function;
|
||||
|
||||
const result = transformFn.call({}, 'console.log("test")', projectPath);
|
||||
|
||||
expect(result).toEqual({
|
||||
code: 'export default {};',
|
||||
map: null,
|
||||
});
|
||||
});
|
||||
|
||||
it('transform should return null for non-.yarnproject files', () => {
|
||||
const plugin = yarnSpinnerVite();
|
||||
const transformFn = plugin.transform as Function;
|
||||
|
||||
const result = transformFn.call({}, 'console.log("test")', 'some-file.ts');
|
||||
|
||||
expect(result).toBeNull();
|
||||
});
|
||||
});
|
||||
|
|
@ -0,0 +1,98 @@
|
|||
import { describe, it, expect, vi } from 'vitest';
|
||||
import { YarnSpinnerWebpackLoader } from '../../src/plugins/webpack';
|
||||
import { resolve } from 'node:path';
|
||||
import { fileURLToPath } from 'node:url';
|
||||
|
||||
const __dirname = fileURLToPath(new URL('.', import.meta.url));
|
||||
|
||||
describe('YarnSpinnerWebpackLoader', () => {
|
||||
// Fixtures are in tests/fixtures/, not tests/plugins/fixtures/
|
||||
const projectPath = resolve(__dirname, '../fixtures/simple/.yarnproject');
|
||||
|
||||
it('should be a function', () => {
|
||||
expect(typeof YarnSpinnerWebpackLoader).toBe('function');
|
||||
});
|
||||
|
||||
it('should load .yarnproject files and return exported JSON', function () {
|
||||
const mockContext = {
|
||||
getOptions: vi.fn().mockReturnValue({}),
|
||||
resourcePath: projectPath,
|
||||
emitError: vi.fn(),
|
||||
};
|
||||
|
||||
const result = YarnSpinnerWebpackLoader.call(mockContext, '');
|
||||
|
||||
expect(result).toBeDefined();
|
||||
expect(typeof result).toBe('string');
|
||||
expect(result).toContain('export default');
|
||||
expect(result).toContain('project');
|
||||
expect(result).toContain('yarnFiles');
|
||||
// Ensure no errors were emitted
|
||||
expect(mockContext.emitError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should pass loadOptions to loadYarnProjectSync', function () {
|
||||
const mockContext = {
|
||||
getOptions: vi.fn().mockReturnValue({
|
||||
loadOptions: {
|
||||
baseDir: resolve(__dirname, 'fixtures/simple'),
|
||||
},
|
||||
}),
|
||||
resourcePath: projectPath,
|
||||
emitError: vi.fn(),
|
||||
};
|
||||
|
||||
const result = YarnSpinnerWebpackLoader.call(mockContext, '');
|
||||
|
||||
expect(result).toContain('export default');
|
||||
expect(mockContext.emitError).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it('should handle errors by calling emitError', function () {
|
||||
const mockContext = {
|
||||
getOptions: vi.fn().mockReturnValue({}),
|
||||
resourcePath: 'nonexistent/.yarnproject',
|
||||
emitError: vi.fn(),
|
||||
};
|
||||
|
||||
const originalSource = '// original source';
|
||||
const result = YarnSpinnerWebpackLoader.call(mockContext, originalSource);
|
||||
|
||||
expect(mockContext.emitError).toHaveBeenCalled();
|
||||
expect(mockContext.emitError).toHaveBeenCalledWith(expect.any(Error));
|
||||
// Should return original source on error
|
||||
expect(result).toBe(originalSource);
|
||||
});
|
||||
|
||||
it('should return original source on error', function () {
|
||||
const originalSource = '// original source';
|
||||
const mockContext = {
|
||||
getOptions: vi.fn().mockReturnValue({}),
|
||||
resourcePath: 'nonexistent/.yarnproject',
|
||||
emitError: vi.fn(),
|
||||
};
|
||||
|
||||
const result = YarnSpinnerWebpackLoader.call(mockContext, originalSource);
|
||||
|
||||
expect(result).toBe(originalSource);
|
||||
});
|
||||
|
||||
it('should handle non-Error objects in error handling', function () {
|
||||
const mockContext = {
|
||||
getOptions: vi.fn().mockReturnValue({}),
|
||||
resourcePath: 'nonexistent/.yarnproject',
|
||||
emitError: vi.fn(),
|
||||
};
|
||||
|
||||
YarnSpinnerWebpackLoader.call(mockContext, '');
|
||||
|
||||
expect(mockContext.emitError).toHaveBeenCalled();
|
||||
const emittedError = mockContext.emitError.mock.calls[0][0];
|
||||
expect(emittedError).toBeInstanceOf(Error);
|
||||
});
|
||||
|
||||
it('should have a default export', () => {
|
||||
// Import using ESM to check default export
|
||||
expect(YarnSpinnerWebpackLoader).toBeDefined();
|
||||
});
|
||||
});
|
||||
Loading…
Reference in New Issue