Skip to content

Commit

Permalink
refactor(cli): migrate from watchService to fs.watch (#2977)
Browse files Browse the repository at this point in the history
- avoid using deprecated fs.watchService. replaced usage with own
- made things sync.
- reduced crazy test timeouts.
- still using non-recursive watcher, but now using fs.watch directly, debouncing events using helper.
- fixed double calling of processFiles during watch event handling (previously raced)
  • Loading branch information
AviVahl authored Dec 22, 2024
1 parent 96c7f14 commit b5a866c
Show file tree
Hide file tree
Showing 27 changed files with 381 additions and 263 deletions.
2 changes: 1 addition & 1 deletion packages/cli/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
"stc-format": "bin/stc-format.js"
},
"scripts": {
"test": "mocha \"./dist/test/**/*.spec.js\" --timeout 25000"
"test": "mocha \"./dist/test/**/*.spec.js\""
},
"dependencies": {
"@file-services/node": "^9.4.1",
Expand Down
6 changes: 3 additions & 3 deletions packages/cli/src/base-generator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -71,12 +71,12 @@ export class IndexGenerator {
);
}

public async generateIndexFile(fs: IFileSystem) {
public generateIndexFile(fs: IFileSystem) {
const indexFileContent = this.generateIndexSource();
ensureDirectory(fs.dirname(this.indexFileTargetPath), fs);

await tryRun(
() => fs.promises.writeFile(this.indexFileTargetPath, '\n' + indexFileContent + '\n'),
tryRun(
() => fs.writeFileSync(this.indexFileTargetPath, '\n' + indexFileContent + '\n'),
'Write Index File Error',
);

Expand Down
19 changes: 11 additions & 8 deletions packages/cli/src/build-stylable.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { nodeFs as fs } from '@file-services/node';
import { nodeFs } from '@file-services/node';
import { Stylable, StylableConfig } from '@stylable/core';
import { StylableResolverCache, validateDefaultConfig } from '@stylable/core/dist/index-internal';
import { build } from './build';
Expand All @@ -12,6 +12,7 @@ import { DiagnosticsManager } from './diagnostics-manager';
import { createDefaultLogger, levels } from './logger';
import type { BuildContext, BuildOptions } from './types';
import { WatchHandler } from './watch-handler';
import { createWatchService } from './watch-service';

export interface BuildStylableContext
extends Partial<Pick<BuildContext, 'fs' | 'watch' | 'log'>>,
Expand All @@ -28,12 +29,12 @@ export interface BuildStylableContext
};
}

export async function buildStylable(
export function buildStylable(
rootDir: string,
{
defaultOptions = createDefaultOptions(),
overrideBuildOptions = {},
fs: fileSystem = fs,
fs = nodeFs,
log = createDefaultLogger(),
watch = false,
resolverCache = new Map(),
Expand All @@ -59,8 +60,9 @@ export async function buildStylable(
const { config } = resolveConfig(rootDir, fs, configFilePath) || {};
validateDefaultConfig(config?.defaultConfig);

const projects = await projectsConfig(rootDir, overrideBuildOptions, defaultOptions, config);
const watchHandler = new WatchHandler(fileSystem, {
const projects = projectsConfig(rootDir, overrideBuildOptions, defaultOptions, config);
const watchService = createWatchService(fs);
const watchHandler = new WatchHandler(fs, watchService, {
log,
resolverCache,
outputFiles,
Expand Down Expand Up @@ -89,7 +91,7 @@ export async function buildStylable(
}

const stylable = new Stylable({
fileSystem,
fileSystem: fs,
requireModule,
projectRoot,
resolverCache,
Expand All @@ -102,11 +104,12 @@ export async function buildStylable(
requireModule('@stylable/node').resolveNamespace,
});

const { service, generatedFiles } = await build(buildOptions, {
const { service, generatedFiles } = build(buildOptions, {
watch,
stylable,
log,
fs: fileSystem,
fs,
watchService,
rootDir,
projectRoot,
outputFiles,
Expand Down
16 changes: 9 additions & 7 deletions packages/cli/src/build.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,9 @@ import { sortModulesByDepth } from '@stylable/build-tools';
import { StylableOptimizer } from '@stylable/optimizer';
import type { Stylable } from '@stylable/core';
import type { IFileSystem } from '@file-services/types';
import { createWatchService } from './watch-service';

export async function build(
export function build(
{
srcDir,
outDir,
Expand Down Expand Up @@ -49,6 +50,7 @@ export async function build(
identifier = _projectRoot,
watch,
fs,
watchService = createWatchService(fs),
stylable,
log,
outputFiles = new Map(),
Expand Down Expand Up @@ -87,7 +89,7 @@ export async function build(
fs,
);

const service = new DirectoryProcessService(fs, {
const service = new DirectoryProcessService(fs, watchService, {
watchMode: watch,
autoResetInvalidations: true,
watchOptions: {
Expand Down Expand Up @@ -129,7 +131,7 @@ export async function build(
throw error;
}
},
async processFiles(_, affectedFiles, deletedFiles, changeOrigin) {
processFiles(_, affectedFiles, deletedFiles, changeOrigin) {
if (changeOrigin) {
// handle deleted files by removing their generated content
if (deletedFiles.size) {
Expand Down Expand Up @@ -196,7 +198,7 @@ export async function build(
// rewire invalidations
updateWatcherDependencies(affectedFiles);
// rebuild assets from aggregated content: index files and assets
await buildAggregatedEntities(affectedFiles, processGeneratedFiles);
buildAggregatedEntities(affectedFiles, processGeneratedFiles);
// rebundle
if (bundle) {
tryRun(() => {
Expand Down Expand Up @@ -242,7 +244,7 @@ export async function build(
},
});

await service.init(fullSrcDir);
service.init(fullSrcDir);

if (sourceFiles.size === 0) {
log(mode, buildMessages.BUILD_SKIPPED(isMultiPackagesProject ? identifier : undefined));
Expand Down Expand Up @@ -362,9 +364,9 @@ export async function build(
});
}

async function buildAggregatedEntities(affectedFiles: Set<string>, generated: Set<string>) {
function buildAggregatedEntities(affectedFiles: Set<string>, generated: Set<string>) {
if (indexFileGenerator) {
await indexFileGenerator.generateIndexFile(fs);
indexFileGenerator.generateIndexFile(fs);

generated.add(indexFileGenerator.indexFileTargetPath);
outputFiles.set(indexFileGenerator.indexFileTargetPath, affectedFiles);
Expand Down
101 changes: 47 additions & 54 deletions packages/cli/src/cli.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,64 +4,57 @@ import { buildStylable } from './build-stylable';
import { createDefaultOptions, getCliArguments, resolveCliOptions } from './config/resolve-options';
import { createLogger } from './logger';

async function main() {
const argv = getCliArguments();
const { resolve } = fs;
const {
watch,
require: requires,
log: shouldLog,
namespaceResolver,
preserveWatchOutput,
config,
} = argv;
const rootDir = resolve(argv.rootDir);
const explicitResolveNs =
namespaceResolver &&
require(
require.resolve(namespaceResolver, {
paths: [rootDir],
}),
);

//
const log = createLogger(
(level, ...messages) => {
if (shouldLog || level === 'info') {
const currentTime = new Date().toLocaleTimeString();
console.log('[Stylable]', `[${currentTime}]`, ...messages);
}
},
() => !shouldLog && !preserveWatchOutput && console.clear(),
const argv = getCliArguments();
const { resolve } = fs;
const {
watch,
require: requires,
log: shouldLog,
namespaceResolver,
preserveWatchOutput,
config,
} = argv;
const rootDir = resolve(argv.rootDir);
const explicitResolveNs =
namespaceResolver &&
require(
require.resolve(namespaceResolver, {
paths: [rootDir],
}),
);

// execute all require hooks before running the CLI build
for (const request of requires) {
require(request);
}
//
const log = createLogger(
(level, ...messages) => {
if (shouldLog || level === 'info') {
const currentTime = new Date().toLocaleTimeString();
console.log('[Stylable]', `[${currentTime}]`, ...messages);
}
},
() => !shouldLog && !preserveWatchOutput && console.clear(),
);

const defaultOptions = createDefaultOptions();
const overrideBuildOptions = resolveCliOptions(argv, defaultOptions);
const { watchHandler } = await buildStylable(rootDir, {
overrideBuildOptions,
defaultOptions,
fs,
resolveNamespace: explicitResolveNs?.resolveNamespace,
watch,
log,
configFilePath: config,
});
// execute all require hooks before running the CLI build
for (const request of requires) {
require(request);
}

process.on('SIGTERM', () => {
void watchHandler.stop();
});
const defaultOptions = createDefaultOptions();
const overrideBuildOptions = resolveCliOptions(argv, defaultOptions);
const { watchHandler } = buildStylable(rootDir, {
overrideBuildOptions,
defaultOptions,
fs,
resolveNamespace: explicitResolveNs?.resolveNamespace,
watch,
log,
configFilePath: config,
});

process.on('SIGINT', () => {
void watchHandler.stop();
});
}
process.on('SIGTERM', () => {
watchHandler.stop();
});

main().catch((e) => {
process.exitCode = 1;
console.error(e);
process.on('SIGINT', () => {
watchHandler.stop();
});
48 changes: 20 additions & 28 deletions packages/cli/src/config/projects-config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import { resolveNpmRequests } from './resolve-requests';
import type { StylableConfig } from '@stylable/core';
import type { IFileSystem } from '@file-services/types';

interface StylableRuntimeConfigs {
export interface StylableRuntimeConfigs {
stcConfig?: Configuration<string> | undefined;
defaultConfig?: Pick<
StylableConfig,
Expand All @@ -28,12 +28,12 @@ interface StylableRuntimeConfigs {
>;
}

export async function projectsConfig(
export function projectsConfig(
rootDir: string,
overrideBuildOptions: Partial<BuildOptions>,
defaultOptions: BuildOptions = createDefaultOptions(),
config?: StylableRuntimeConfigs,
): Promise<STCProjects> {
): STCProjects {
const topLevelOptions = mergeBuildOptions(
defaultOptions,
config?.stcConfig?.options,
Expand All @@ -42,29 +42,21 @@ export async function projectsConfig(

validateOptions(topLevelOptions);

let projects: STCProjects;

if (isMultipleConfigProject(config)) {
const { entities } = processProjects(config.stcConfig, {
defaultOptions: topLevelOptions,
});

projects = await resolveProjectsRequests({
rootDir,
entities,
resolveRequests:
config.stcConfig.projectsOptions?.resolveRequests ?? resolveNpmRequests,
});
} else {
projects = [
{
projectRoot: rootDir,
options: [topLevelOptions],
},
];
}

return projects;
return isMultipleConfigProject(config)
? resolveProjectsRequests({
rootDir,
entities: processProjects(config.stcConfig, {
defaultOptions: topLevelOptions,
}).entities,
resolveRequests:
config.stcConfig.projectsOptions?.resolveRequests ?? resolveNpmRequests,
})
: [
{
projectRoot: rootDir,
options: [topLevelOptions],
},
];
}

export function resolveConfig(context: string, fs: IFileSystem, request?: string) {
Expand Down Expand Up @@ -110,15 +102,15 @@ function isMultipleConfigProject(
return Boolean(config?.stcConfig?.projects);
}

async function resolveProjectsRequests({
function resolveProjectsRequests({
entities,
rootDir,
resolveRequests,
}: {
rootDir: string;
entities: Array<RawProjectEntity>;
resolveRequests: ResolveRequests;
}): Promise<STCProjects> {
}): STCProjects {
const context: ResolveProjectsContext = { rootDir };

return resolveRequests(entities, context);
Expand Down
Loading

0 comments on commit b5a866c

Please sign in to comment.