Skip to content

Commit

Permalink
feat!: move to file-services/resolve as default resolver
Browse files Browse the repository at this point in the history
  • Loading branch information
barak007 committed Sep 26, 2023
1 parent 30b01f5 commit e5fbc32
Show file tree
Hide file tree
Showing 42 changed files with 207 additions and 133 deletions.
20 changes: 20 additions & 0 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion packages/cli/src/cli-codemod.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
#!/usr/bin/env node

import fs from 'fs';
import fs from '@file-services/node';
import { resolve } from 'path';
import yargs from 'yargs';
import { codeMods } from './code-mods/code-mods';
Expand Down
11 changes: 5 additions & 6 deletions packages/cli/src/code-format.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,10 @@
#!/usr/bin/env node
import yargs from 'yargs';
import { nodeFs } from '@file-services/node';
import fs from '@file-services/node';
import { getDocumentFormatting, formatCSS } from '@stylable/code-formatter';
import { createLogger } from './logger';
import { writeFileSync } from 'fs';

const { join } = nodeFs;
const { join, writeFileSync } = fs;

const argv = yargs
.usage('$0 [options]')
Expand Down Expand Up @@ -134,7 +133,7 @@ for (const request of requires) {
}

function readDirectoryDeep(dirPath: string, fileSuffixFilter = '.st.css', res = new Set<string>()) {
const items = nodeFs.readdirSync(dirPath, { withFileTypes: true });
const items = fs.readdirSync(dirPath, { withFileTypes: true });

for (const item of items) {
const path = join(dirPath, item.name);
Expand All @@ -150,7 +149,7 @@ function readDirectoryDeep(dirPath: string, fileSuffixFilter = '.st.css', res =
}

function formatStylesheet(filePath: string) {
const fileContent = nodeFs.readFileSync(filePath, 'utf-8');
const fileContent = fs.readFileSync(filePath, 'utf-8');

const newText = experimental
? formatCSS(fileContent, {
Expand Down Expand Up @@ -188,7 +187,7 @@ if (debug) {
log('Starting code formatting');
}

const formatPathStats = nodeFs.statSync(target);
const formatPathStats = fs.statSync(target);

if (formatPathStats.isFile()) {
if (target.endsWith('.st.css')) {
Expand Down
9 changes: 5 additions & 4 deletions packages/cli/src/config/projects-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import { createDefaultOptions, mergeBuildOptions, validateOptions } from './reso
import { resolveNpmRequests } from './resolve-requests';
import type { ModuleResolver } from '@stylable/core/dist/index-internal';
import type { MinimalFS, processNamespace } from '@stylable/core';
import type { IFileSystem } from '@file-services/types';

interface StylableRuntimeConfigs {
stcConfig?: Configuration<string> | undefined;
Expand Down Expand Up @@ -64,21 +65,21 @@ export async function projectsConfig(
}

// todo: make fs not optional next major version
export function resolveConfig(context: string, request?: string, fs?: MinimalFS) {
export function resolveConfig(context: string, request?: string, fs?: IFileSystem | MinimalFS) {
return request ? requireConfigFile(request, context, fs) : resolveConfigFile(context, fs);
}

function requireConfigFile(request: string, context: string, fs?: MinimalFS) {
function requireConfigFile(request: string, context: string, fs?: IFileSystem | MinimalFS) {
const path = require.resolve(request, { paths: [context] });
const config = resolveConfigValue(require(path), fs);
return config ? { config, path } : undefined;
}

function resolveConfigFile(context: string, fs?: MinimalFS) {
function resolveConfigFile(context: string, fs?: IFileSystem | MinimalFS) {
return loadStylableConfig(context, (config) => resolveConfigValue(config, fs));
}

function resolveConfigValue(config: any, fs?: MinimalFS) {
function resolveConfigValue(config: any, fs?: IFileSystem | MinimalFS) {
return tryRun(
(): StylableRuntimeConfigs => ({
stcConfig: isSTCConfig(config)
Expand Down
2 changes: 1 addition & 1 deletion packages/cli/test/assets.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ describe('assets', function () {
'/src/other.st.css': '.other {}',
'/node_modules/styles/3rd-party.css': '.third-party {}',
});
const resolve = createDefaultResolver(fs, {});
const resolve = createDefaultResolver({ fs });
const stylable = new Stylable({
projectRoot: '/',
fileSystem: fs,
Expand Down
4 changes: 2 additions & 2 deletions packages/cli/test/build.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -682,7 +682,7 @@ describe('build stand alone', () => {
expect(dtsSourceMapContent).to.contain(`"main.st.css"`);
});

describe('resolver', () => {
describe('resolver (build)', () => {
it('should be able to build with enhanced-resolver alias configured', async () => {
const identifier = 'build-identifier';
const fs = createMemoryFs({
Expand All @@ -701,7 +701,7 @@ describe('build stand alone', () => {
requireModule: () => ({}),
resolveOptions: {
alias: {
'@colors': '/colors',
'@colors/*': '/colors/*',
},
},
});
Expand Down
10 changes: 5 additions & 5 deletions packages/cli/test/cli.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -558,18 +558,18 @@ describe('Stylable Cli', function () {
});
});

describe('resolver', () => {
describe('resolver (cli)', () => {
it('should be able to build with enhanced-resolver alias configured', () => {
populateDirectorySync(tempDir.path, {
'package.json': `{"name": "test", "version": "0.0.0"}`,
'stylable.config.js': `
const { resolve } = require('node:path');
const { createDefaultResolver } = require('@stylable/core');
const { createLegacyResolver } = require('@stylable/core');
module.exports = {
defaultConfig(fs) {
return {
resolveModule: createDefaultResolver(fs, {
resolveModule: createLegacyResolver(fs, {
alias: {
'@colors': resolve(__dirname, './colors')
}
Expand Down Expand Up @@ -618,12 +618,12 @@ describe('Stylable Cli', function () {
'stylable.config.js': `
const { join } = require('path');
const { TsconfigPathsPlugin } = require('tsconfig-paths-webpack-plugin');
const { createDefaultResolver } = require('@stylable/core');
const { createLegacyResolver } = require('@stylable/core');
module.exports = {
defaultConfig(fs) {
return {
resolveModule: createDefaultResolver(fs, {
resolveModule: createLegacyResolver(fs, {
plugins: [new TsconfigPathsPlugin({ configFile: join(${JSON.stringify(
tempDir.path
)},'tsconfig.json') })],
Expand Down
2 changes: 1 addition & 1 deletion packages/core-test-kit/src/generate-test-util.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ export function generateInfra(config: InfraConfig, diagnostics: Diagnostics = ne
createDiagnostics: () => diagnostics,
});

const resolveModule = createDefaultResolver(fs, {});
const resolveModule = createDefaultResolver({ fs });
const resolvePath = (context: string | undefined = '/', moduleId: string) =>
resolveModule(context, moduleId);

Expand Down
2 changes: 1 addition & 1 deletion packages/core-test-kit/test/inline-expectations.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1021,7 +1021,7 @@ describe('inline-expectations', () => {
);
});
it(`should throw on possible location mismatch`, () => {
const resolveErrorMessage = `resolve './unknown.st.css' in '/'\n No description file found in / or above\n No description file found in / or above\n no extension\n /unknown.st.css doesn't exist\n .js\n /unknown.st.css.js doesn't exist\n .json\n /unknown.st.css.json doesn't exist\n .node\n /unknown.st.css.node doesn't exist\n as directory\n /unknown.st.css doesn't exist`;
const resolveErrorMessage = 'Stylable could not resolve "./unknown.st.css" from "/"';
const result = generateStylableResult({
entry: `/style.st.css`,
files: {
Expand Down
1 change: 1 addition & 0 deletions packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"module": false
},
"dependencies": {
"@file-services/resolve": "^8.2.0",
"@tokey/css-selector-parser": "^0.6.2",
"@tokey/css-value-parser": "^0.1.4",
"@tokey/imports-parser": "^1.0.0",
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ export {
} from './helpers/namespace';
export { processNamespace } from './stylable-processor';
export { CustomValueStrategy, createCustomValue } from './custom-values';
export { createLegacyResolver } from './legacy-module-resolver';
export { createDefaultResolver } from './module-resolver';

// low-level api
Expand Down
47 changes: 47 additions & 0 deletions packages/core/src/legacy-module-resolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// in browser build this gets remapped to an empty object via our package.json->"browser"
import nodeModule from 'module';
// importing the factory directly, as we feed it our own fs, and don't want graceful-fs to be implicitly imported
// this allows @stylable/core to be bundled for browser usage without special custom configuration
import ResolverFactory from 'enhanced-resolve/lib/ResolverFactory.js';
import type { ModuleResolver } from './types';
import type { MinimalFS } from './cached-process-file';

function bundleSafeRequireExtensions(): string[] {
let extensions: string[];
try {
// we use nodeModule here to avoid bundling warnings about require.extensions we always has fallback for browsers
extensions = Object.keys(
(nodeModule as typeof nodeModule & { _extensions?: Record<string, unknown> })
._extensions ?? {}
);
} catch (e) {
extensions = [];
}
return extensions.length ? extensions : ['.js', '.json'];
}

const resolverContext = {};

export function createLegacyResolver(fileSystem: MinimalFS, resolveOptions: any): ModuleResolver {
const extensions =
resolveOptions.extensions && resolveOptions.extensions.length
? resolveOptions.extensions
: bundleSafeRequireExtensions();
const eResolver = ResolverFactory.createResolver({
...resolveOptions,
extensions,
useSyncFileSystemCalls: true,
cache: false,
fileSystem,
});

return (directoryPath, request): string => {
const res = eResolver.resolveSync(resolverContext, directoryPath, request);
if (res === false) {
throw new Error(
`Stylable does not support browser field 'false' values. ${request} resolved to 'false' from ${directoryPath}`
);
}
return res;
};
}
47 changes: 12 additions & 35 deletions packages/core/src/module-resolver.ts
Original file line number Diff line number Diff line change
@@ -1,47 +1,24 @@
// in browser build this gets remapped to an empty object via our package.json->"browser"
import nodeModule from 'module';
// importing the factory directly, as we feed it our own fs, and don't want graceful-fs to be implicitly imported
// this allows @stylable/core to be bundled for browser usage without special custom configuration
import ResolverFactory from 'enhanced-resolve/lib/ResolverFactory.js';
import type { ModuleResolver } from './types';
import type { MinimalFS } from './cached-process-file';

function bundleSafeRequireExtensions(): string[] {
let extensions: string[];
try {
// we use nodeModule here to avoid bundling warnings about require.extensions we always has fallback for browsers
extensions = Object.keys(
(nodeModule as typeof nodeModule & { _extensions?: Record<string, unknown> })
._extensions ?? {}
);
} catch (e) {
extensions = [];
}
return extensions.length ? extensions : ['.js', '.json'];
}

const resolverContext = {};
import { IRequestResolverOptions, createRequestResolver } from '@file-services/resolve';
import type { ModuleResolver } from './types';

export function createDefaultResolver(fileSystem: MinimalFS, resolveOptions: any): ModuleResolver {
const extensions =
resolveOptions.extensions && resolveOptions.extensions.length
? resolveOptions.extensions
: bundleSafeRequireExtensions();
const eResolver = ResolverFactory.createResolver({
...resolveOptions,
extensions,
useSyncFileSystemCalls: true,
cache: false,
fileSystem,
export function createDefaultResolver(options: IRequestResolverOptions): ModuleResolver {
const resolver = createRequestResolver({
extensions: ['.js', '.json', '.mjs', '.cjs', '.ts', '.mts', '.cts'],
...options,
});

return (directoryPath, request): string => {
const res = eResolver.resolveSync(resolverContext, directoryPath, request);
if (res === false) {
const res = resolver(directoryPath, request);
if (res.resolvedFile === false) {
throw new Error(
`Stylable does not support browser field 'false' values. ${request} resolved to 'false' from ${directoryPath}`
);
}
return res;
if (typeof res.resolvedFile !== 'string') {
throw new Error(`Stylable could not resolve ${JSON.stringify(request)} from ${JSON.stringify(directoryPath)}`);
}
return res.resolvedFile;
};
}
4 changes: 3 additions & 1 deletion packages/core/src/stylable.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ export interface StylableConfig {
requireModule?: (path: string) => any;
onProcess?: (meta: StylableMeta, path: string) => StylableMeta;
hooks?: TransformHooks;
/** @deprecated provide a `resolveModule` instead */
resolveOptions?: {
alias?: any;
symlinks?: boolean;
Expand Down Expand Up @@ -105,7 +106,8 @@ export class Stylable {
this.mode = config.mode || `production`;
this.resolveNamespace = config.resolveNamespace;
this.moduleResolver =
config.resolveModule || createDefaultResolver(this.fileSystem, this.resolveOptions);
config.resolveModule ||
createDefaultResolver({ fs: this.fileSystem, ...this.resolveOptions });
this.cssParser = config.cssParser || cssParse;
this.resolverCache = config.resolverCache; // ToDo: v5 default to `new Map()`
this.fileProcessorCache = config.fileProcessorCache;
Expand Down
10 changes: 5 additions & 5 deletions packages/core/test/features/st-import.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -211,8 +211,8 @@ describe(`features/st-import`, () => {
`);
});
it(`should error on unresolved file`, () => {
const resolveErrorMessage = `resolve './missing.st.css' in '/'\n No description file found in / or above\n No description file found in / or above\n no extension\n /missing.st.css doesn't exist\n .js\n /missing.st.css.js doesn't exist\n .json\n /missing.st.css.json doesn't exist\n .node\n /missing.st.css.node doesn't exist\n as directory\n /missing.st.css doesn't exist`;
const resolveErrorMessagePackage = `resolve 'missing-package/index.st.css' in '/'\n Parsed request is a module\n No description file found in / or above\n resolve as module\n /node_modules doesn't exist or is not a directory`;
const resolveErrorMessage = `Stylable could not resolve "./missing.st.css" from "/"`;
const resolveErrorMessagePackage = `Stylable could not resolve "missing-package/index.st.css" from "/"`;

testStylableCore(`
/* @transform-error(relative) word(./missing.st.css) ${stImportDiagnostics.UNKNOWN_IMPORTED_FILE(
Expand Down Expand Up @@ -533,9 +533,9 @@ describe(`features/st-import`, () => {
`);
});
it(`should error on unresolved file`, () => {
const resolveErrorMessage = `resolve './missing.st.css' in '/'\n No description file found in / or above\n No description file found in / or above\n no extension\n /missing.st.css doesn't exist\n .js\n /missing.st.css.js doesn't exist\n .json\n /missing.st.css.json doesn't exist\n .node\n /missing.st.css.node doesn't exist\n as directory\n /missing.st.css doesn't exist`;
const resolveErrorMessagePackage = `resolve 'missing-package/index.st.css' in '/'\n Parsed request is a module\n No description file found in / or above\n resolve as module\n /node_modules doesn't exist or is not a directory`;

const resolveErrorMessage = `Stylable could not resolve "./missing.st.css" from "/"`;
const resolveErrorMessagePackage = `Stylable could not resolve "missing-package/index.st.css" from "/"`;
testStylableCore(`
:import{
/* @transform-error(relative) word(./missing.st.css) ${stImportDiagnostics.UNKNOWN_IMPORTED_FILE(
Expand Down
Loading

0 comments on commit e5fbc32

Please sign in to comment.