Skip to content

Commit

Permalink
fix(monorepos): do not lazy require missing dependencies
Browse files Browse the repository at this point in the history
- Do not auto-install any missing dev dependencies, like jest or puppeteer
- If a dev dep is missing, print out an error what needs to be installed
- addWatchDir / addWatchFile to compilerCtx
- Watch for changes in global styles and global style imports
  • Loading branch information
adamdbradley committed Aug 26, 2020
1 parent 43c5d98 commit 7f739a0
Show file tree
Hide file tree
Showing 12 changed files with 223 additions and 347 deletions.
9 changes: 5 additions & 4 deletions src/cli/task-test.ts
Expand Up @@ -36,10 +36,11 @@ export const taskTest = async (config: Config) => {
}

// ensure we've got the required modules installed
// jest and puppeteer are quite large, so this
// is an experiment to lazy install these
// modules only when you need them
await config.sys.lazyRequire.ensure(config.logger, config.rootDir, ensureModuleIds);
const diagnostics = await config.sys.lazyRequire.ensure(config.rootDir, ensureModuleIds);
if (diagnostics.length > 0) {
config.logger.printDiagnostics(diagnostics);
return config.sys.exit(1);
}

// let's test!
const { createTesting } = await import('@stencil/core/testing');
Expand Down
21 changes: 7 additions & 14 deletions src/compiler/build/compiler-ctx.ts
@@ -1,7 +1,7 @@
import type * as d from '../../declarations';
import { basename, dirname, extname, join } from 'path';
import { buildEvents } from '../events';
import { normalizePath } from '@utils';
import { noop, normalizePath } from '@utils';

/**
* The CompilerCtx is a persistent object that's reused throughout
Expand All @@ -17,33 +17,28 @@ export class CompilerContext implements d.CompilerCtx {
activeFilesUpdated: string[] = [];
activeDirsAdded: string[] = [];
activeDirsDeleted: string[] = [];
addWatchDir: (path: string) => void = noop;
addWatchFile: (path: string) => void = noop;
cache: d.Cache;
cachedStyleMeta = new Map<string, d.StyleCompiler>();
changedFiles = new Set<string>();
changedModules = new Set<string>();
collections: d.CollectionCompilerMeta[] = [];
compilerOptions: any = null;
events = buildEvents();
fs: d.InMemoryFileSystem;
fsWatcher: d.FsWatcher = null;
hasFsWatcherEvents = false;
hasLoggedServerUrl = false;
hasSuccessfulBuild = false;
isActivelyBuilding = false;
lastBuildResults: d.CompilerBuildResults = null;
lastBuildStyles = new Map<string, string>();
lastComponentStyleInput = new Map<string, string>();
moduleMap: d.ModuleMap = new Map();
nodeMap = new WeakMap();
resolvedCollections = new Set<string>();
rollupCache = new Map();
rollupCacheHydrate: any = null;
rollupCacheLazy: any = null;
rollupCacheNative: any = null;
rootTsFiles: string[] = [];
tsService: d.TsService = null;
cachedGlobalStyle: string;
styleModeNames = new Set<string>();
rollupCache = new Map();
changedModules = new Set<string>();
changedFiles = new Set<string>();
worker: d.CompilerWorkerContext = null;

reset() {
Expand All @@ -52,14 +47,12 @@ export class CompilerContext implements d.CompilerCtx {
this.cachedGlobalStyle = null;
this.collections.length = 0;
this.compilerOptions = null;
this.lastComponentStyleInput.clear();
this.hasSuccessfulBuild = false;
this.rollupCacheHydrate = null;
this.rollupCacheLazy = null;
this.rollupCacheNative = null;
this.moduleMap.clear();
this.resolvedCollections.clear();
this.rootTsFiles.length = 0;
this.tsService = null;

if (this.fs != null) {
this.fs.clearCache();
Expand Down
207 changes: 99 additions & 108 deletions src/compiler/build/watch-build.ts
Expand Up @@ -4,16 +4,24 @@ import { BuildContext } from './build-ctx';
import { compilerRequest } from '../bundle/dev-module';
import { createTsWatchProgram } from '../transpile/create-watch-program';
import { dirname, resolve } from 'path';
import { filesChanged, hasHtmlChanges, hasScriptChanges, hasStyleChanges, scriptsAdded, scriptsDeleted } from '../fs-watch/fs-watch-rebuild';
import {
filesChanged,
hasHtmlChanges,
hasScriptChanges,
hasStyleChanges,
scriptsAdded,
scriptsDeleted,
} from '../fs-watch/fs-watch-rebuild';
import { hasServiceWorkerChanges } from '../service-worker/generate-sw';
import { isString } from '@utils';
import type ts from 'typescript';

export const createWatchBuild = async (config: d.Config, compilerCtx: d.CompilerCtx): Promise<d.CompilerWatcher> => {
let isRebuild = false;
let tsWatchProgram: { program: ts.WatchOfConfigFile<ts.EmitAndSemanticDiagnosticsBuilderProgram>; rebuild: () => void };
let srcFileWatchCloser: () => void;
let otherFileWatchCloser: () => void;

let tsWatchProgram: {
program: ts.WatchOfConfigFile<ts.EmitAndSemanticDiagnosticsBuilderProgram>;
rebuild: () => void;
};
let closeResolver: Function;
const watchWaiter = new Promise<d.WatcherCloseResults>(resolve => (closeResolver = resolve));

Expand All @@ -23,31 +31,6 @@ export const createWatchBuild = async (config: d.Config, compilerCtx: d.Compiler
const filesUpdated = new Set<string>();
const filesDeleted = new Set<string>();

const onSrcFileChange: d.CompilerFileWatcherCallback = (p, eventKind) => {
updateCompilerCtxCache(config, compilerCtx, p, eventKind);

switch (eventKind) {
case 'dirAdd':
dirsAdded.add(p);
break;
case 'dirDelete':
dirsDeleted.add(p);
break;
case 'fileAdd':
filesAdded.add(p);
break;
case 'fileUpdate':
filesUpdated.add(p);
break;
case 'fileDelete':
filesDeleted.add(p);
break;
}

config.logger.debug(`${eventKind}: ${p}`);
tsWatchProgram.rebuild();
};

const onBuild = async (tsBuilder: ts.BuilderProgram) => {
const buildCtx = new BuildContext(config, compilerCtx);
buildCtx.isRebuild = isRebuild;
Expand Down Expand Up @@ -83,27 +66,61 @@ export const createWatchBuild = async (config: d.Config, compilerCtx: d.Compiler
};

const start = async () => {
const srcRead = watchSrcDirectory(config, compilerCtx, onSrcFileChange);
const otherRead = watchOtherFiles(config, compilerCtx);
srcFileWatchCloser = await srcRead;
otherFileWatchCloser = await otherRead;
const srcRead = watchSrcDirectory(config, compilerCtx);
const otherRead = watchRootFiles(config, compilerCtx);
await srcRead;
await otherRead;
tsWatchProgram = await createTsWatchProgram(config, onBuild);
return watchWaiter;
};

const close = async () => {
if (srcFileWatchCloser) {
srcFileWatchCloser();
const watchingDirs = new Map<string, d.CompilerFileWatcher>();
const watchingFiles = new Map<string, d.CompilerFileWatcher>();

const onFsChange: d.CompilerFileWatcherCallback = (p, eventKind) => {
if (tsWatchProgram) {
updateCompilerCtxCache(config, compilerCtx, p, eventKind);

switch (eventKind) {
case 'dirAdd':
dirsAdded.add(p);
break;
case 'dirDelete':
dirsDeleted.add(p);
break;
case 'fileAdd':
filesAdded.add(p);
break;
case 'fileUpdate':
filesUpdated.add(p);
break;
case 'fileDelete':
filesDeleted.add(p);
break;
}

config.logger.debug(`onFsChange ${eventKind}: ${p}`);
tsWatchProgram.rebuild();
}
if (otherFileWatchCloser) {
otherFileWatchCloser();
};

const onDirChange: d.CompilerFileWatcherCallback = (p, eventKind) => {
if (eventKind != null) {
onFsChange(p, eventKind);
}
};

const close = async () => {
watchingDirs.forEach(w => w.close());
watchingFiles.forEach(w => w.close());
watchingDirs.clear();
watchingFiles.clear();

if (tsWatchProgram) {
tsWatchProgram.program.close();
tsWatchProgram = null;
}

srcFileWatchCloser = otherFileWatchCloser = tsWatchProgram = null;

const watcherCloseResults: d.WatcherCloseResults = {
exitCode: 0,
};
Expand All @@ -113,6 +130,18 @@ export const createWatchBuild = async (config: d.Config, compilerCtx: d.Compiler

const request = async (data: d.CompilerRequest) => compilerRequest(config, compilerCtx, data);

compilerCtx.addWatchFile = filePath => {
if (isString(filePath) && !watchingFiles.has(filePath)) {
watchingFiles.set(filePath, config.sys.watchFile(filePath, onFsChange));
}
};

compilerCtx.addWatchDir = (dirPath, recursive) => {
if (isString(dirPath) && !watchingDirs.has(dirPath)) {
watchingDirs.set(dirPath, config.sys.watchDirectory(dirPath, onDirChange, recursive));
}
};

config.sys.addDestory(close);

return {
Expand All @@ -123,90 +152,47 @@ export const createWatchBuild = async (config: d.Config, compilerCtx: d.Compiler
};
};

const watchSrcDirectory = async (config: d.Config, compilerCtx: d.CompilerCtx, callback: d.CompilerFileWatcherCallback) => {
const watching = new Map();
const watchFile = (path: string) => {
if (!watching.has(path)) {
watching.set(path, config.sys.watchFile(path, callback));
}
};

const watchSrcDirectory = async (config: d.Config, compilerCtx: d.CompilerCtx) => {
const srcFiles = await compilerCtx.fs.readdir(config.srcDir, {
recursive: true,
excludeDirNames: ['.cache', '.git', '.github', '.stencil', '.vscode', 'node_modules'],
excludeExtensions: ['.md', '.markdown', '.txt', '.spec.ts', '.spec.tsx', '.e2e.ts', '.e2e.tsx', '.gitignore', '.editorconfig'],
excludeExtensions: [
'.md',
'.markdown',
'.txt',
'.spec.ts',
'.spec.tsx',
'.e2e.ts',
'.e2e.tsx',
'.gitignore',
'.editorconfig',
],
});

srcFiles.filter(({ isFile }) => isFile).forEach(({ absPath }) => watchFile(absPath));

watching.set(
config.srcDir,
config.sys.watchDirectory(config.srcDir, (filename, kind) => {
if (kind != null) {
watchFile(filename);
callback(filename, kind);
}
}),
);
srcFiles.filter(({ isFile }) => isFile).forEach(({ absPath }) => compilerCtx.addWatchFile(absPath));

return () => {
watching.forEach(w => w.close());
};
compilerCtx.addWatchDir(config.srcDir, true);
};

const watchOtherFiles = async (config: d.Config, compilerCtx: d.CompilerCtx) => {
const watchRootFiles = async (config: d.Config, compilerCtx: d.CompilerCtx) => {
// non-src files that cause a rebuild
// mainly for root level config files, and getting an event when they change
const onFileChange: d.CompilerFileWatcherCallback = (p, eventKind) => {
const data: d.FsWatchResults = {
dirsAdded: [],
dirsDeleted: [],
filesUpdated: [],
filesAdded: [],
filesDeleted: [],
};

switch (eventKind) {
case 'dirAdd':
data.dirsAdded.push(p);
break;
case 'dirDelete':
data.dirsDeleted.push(p);
break;
case 'fileAdd':
data.filesAdded.push(p);
break;
case 'fileUpdate':
data.filesUpdated.push(p);
break;
case 'fileDelete':
data.filesDeleted.push(p);
break;
}

compilerCtx.events.emit('fsChange', data);
};
const watching = new Map();
const watchFile = (path: string) => {
if (!watching.has(path)) {
watching.set(path, config.sys.watchFile(path, onFileChange));
}
};

const rootFiles = await compilerCtx.fs.readdir(config.rootDir, {
recursive: false,
excludeDirNames: ['.cache', '.git', '.github', '.stencil', '.vscode', 'node_modules'],
});

rootFiles.filter(({ isFile }) => isFile).forEach(({ absPath }) => watchFile(absPath));

return () => {
watching.forEach(w => w.close());
};
rootFiles.filter(({ isFile }) => isFile).forEach(({ absPath }) => compilerCtx.addWatchFile(absPath));
};

const emitFsChange = (compilerCtx: d.CompilerCtx, buildCtx: BuildContext) => {
if (buildCtx.dirsAdded.length > 0 || buildCtx.dirsDeleted.length > 0 || buildCtx.filesUpdated.length > 0 || buildCtx.filesAdded.length > 0 || buildCtx.filesDeleted.length > 0) {
if (
buildCtx.dirsAdded.length > 0 ||
buildCtx.dirsDeleted.length > 0 ||
buildCtx.filesUpdated.length > 0 ||
buildCtx.filesAdded.length > 0 ||
buildCtx.filesDeleted.length > 0
) {
compilerCtx.events.emit('fsChange', {
dirsAdded: buildCtx.dirsAdded.slice(),
dirsDeleted: buildCtx.dirsDeleted.slice(),
Expand All @@ -217,7 +203,12 @@ const emitFsChange = (compilerCtx: d.CompilerCtx, buildCtx: BuildContext) => {
}
};

const updateCompilerCtxCache = (config: d.Config, compilerCtx: d.CompilerCtx, path: string, kind: d.CompilerFileWatcherEvent) => {
const updateCompilerCtxCache = (
config: d.Config,
compilerCtx: d.CompilerCtx,
path: string,
kind: d.CompilerFileWatcherEvent,
) => {
compilerCtx.fs.clearFileCache(path);
compilerCtx.changedFiles.add(path);

Expand Down
8 changes: 7 additions & 1 deletion src/compiler/bundle/ext-transforms-plugin.ts
Expand Up @@ -7,7 +7,12 @@ import { parseImportPath } from '../transformers/stencil-import-path';
import type { Plugin } from 'rollup';
import { runPluginTransformsEsmImports } from '../plugin/plugin';

export const extTransformsPlugin = (config: d.Config, compilerCtx: d.CompilerCtx, buildCtx: d.BuildCtx, bundleOpts: BundleOptions): Plugin => {
export const extTransformsPlugin = (
config: d.Config,
compilerCtx: d.CompilerCtx,
buildCtx: d.BuildCtx,
bundleOpts: BundleOptions,
): Plugin => {
return {
name: 'extTransformsPlugin',

Expand Down Expand Up @@ -67,6 +72,7 @@ export const extTransformsPlugin = (config: d.Config, compilerCtx: d.CompilerCtx
// Track dependencies
for (const dep of pluginTransforms.dependencies) {
this.addWatchFile(dep);
compilerCtx.addWatchFile(dep);
}

buildCtx.diagnostics.push(...pluginTransforms.diagnostics);
Expand Down

0 comments on commit 7f739a0

Please sign in to comment.