Skip to content

Commit

Permalink
Instrument eyeglass for perf analysis with heimdalljs.
Browse files Browse the repository at this point in the history
  • Loading branch information
chriseppstein committed Mar 8, 2019
1 parent b906ac2 commit 70577ca
Show file tree
Hide file tree
Showing 9 changed files with 146 additions and 81 deletions.
7 changes: 6 additions & 1 deletion eyeglass.code-workspace
Expand Up @@ -9,5 +9,10 @@
{
"path": "packages/ember-cli-eyeglass"
}
]
],
"settings": {
"cSpell.words": [
"heimdall"
]
}
}
56 changes: 32 additions & 24 deletions packages/eyeglass/src/Eyeglass.ts
Expand Up @@ -14,6 +14,7 @@ import { SassFunction } from "node-sass";
import { SassImplementation, helpers as sassHelpers } from "./util/SassImplementation";
import { AsyncImporter } from "node-sass";
import { UnsafeDict } from "./util/typescriptUtils";
import heimdall = require("heimdalljs");
// eslint-disable-next-line @typescript-eslint/no-var-requires
const pkg: PackageJson = require("../package.json");

Expand All @@ -26,39 +27,46 @@ export default class Eyeglass implements IEyeglass {
modules: EyeglassModules;

constructor(options: Opts, deprecatedNodeSassArg?: SassImplementation) {
let timer = heimdall.start("eyeglass:instantiation");
try {
// an interface for deprecation warnings
this.deprecate = deprecator(options);

// an interface for deprecation warnings
this.deprecate = deprecator(options);
this.options = new Options(options, this.deprecate, deprecatedNodeSassArg);
this.assets = new Assets(this, this.options.eyeglass.engines.sass);
this.modules = new EyeglassModules(
this.options.eyeglass.root,
this.options,
this.options.eyeglass.modules,
);

this.options = new Options(options, this.deprecate, deprecatedNodeSassArg);
this.assets = new Assets(this, this.options.eyeglass.engines.sass);
this.modules = new EyeglassModules(
this.options.eyeglass.root,
this.options,
this.options.eyeglass.modules,
);

fs.mkdirpSync(this.options.eyeglass.cacheDir);
fs.mkdirpSync(this.options.eyeglass.cacheDir);

semverChecker(this, this.options.eyeglass.engines.sass, this.options.eyeglass, Eyeglass.VERSION);
semverChecker(this, this.options.eyeglass.engines.sass, this.options.eyeglass, Eyeglass.VERSION);

checkMissingDependencies.call(this);
checkMissingDependencies.call(this);

// initialize all the modules
this.modules.init(this, this.options.eyeglass.engines.sass);
// initialize all the modules
this.modules.init(this, this.options.eyeglass.engines.sass);

// add importers and functions
addImporters.call(this);
addFunctions.call(this);
// add importers and functions
addImporters.call(this);
addFunctions.call(this);

// deprecated stuff
deprecateProperties.call(this, ["enableImportOnce"]);
// deprecated stuff
deprecateProperties.call(this, ["enableImportOnce"]);

// auto-add asset paths specified via options
if (this.options.eyeglass.assets.sources) {
for (let assetSource of this.options.eyeglass.assets.sources) {
this.assets.addSource(assetSource.directory, assetSource);
// auto-add asset paths specified via options
if (this.options.eyeglass.assets.sources) {
for (let assetSource of this.options.eyeglass.assets.sources) {
this.assets.addSource(assetSource.directory, assetSource);
}
}
} catch(e) {
// typescript needs this catch & throw to convince it that the instance properties are initialized.
throw e;
} finally {
timer.stop();
}
}

Expand Down
2 changes: 1 addition & 1 deletion packages/eyeglass/src/importers/AssetImporter.ts
Expand Up @@ -19,7 +19,7 @@ interface HasAssets {
const rAssetsImport = /^(?:([^/]+)\/)?assets$/;
const AssetImporter: ImporterFactory = function (eyeglass, sass, options, fallbackImporter?: AsyncImporter | Array<AsyncImporter>): AsyncImporter {

return ImportUtilities.createImporter(function(uri, prev, done) {
return ImportUtilities.createImporter("assets", function(uri, prev, done) {
let importUtils = new ImportUtilities(eyeglass, sass, options, fallbackImporter, this);

let isRealFile = existsSync(prev);
Expand Down
2 changes: 1 addition & 1 deletion packages/eyeglass/src/importers/FSImporter.ts
Expand Up @@ -7,7 +7,7 @@ import { AsyncImporter } from "node-sass";
const FSImporter: ImporterFactory = function (eyeglass, sass, options, fallbackImporter): AsyncImporter {
let fsURI = /^fs\(([-_a-zA-Z][-_a-zA-Z0-9]+)\)$/;

return ImportUtilities.createImporter(function(uri, prev, done) {
return ImportUtilities.createImporter("fs", function(uri, prev, done) {
let importUtils = new ImportUtilities(eyeglass, sass, options, fallbackImporter, this);
let match = uri.match(fsURI);
if (match) {
Expand Down
10 changes: 8 additions & 2 deletions packages/eyeglass/src/importers/ImportUtilities.ts
Expand Up @@ -7,6 +7,7 @@ import { SassImplementation } from "../util/SassImplementation";
import { Config } from "../util/Options";
import { isPresent, Dict } from "../util/typescriptUtils";
import { ImportedFile } from "./ImporterFactory";
import heimdall = require("heimdalljs");

type ImportContext = AsyncContext & {eyeglass: {imported: Dict<boolean>}};

Expand All @@ -29,8 +30,13 @@ export default class ImportUtilities {
}
});
}
static createImporter(importer: AsyncImporter): AsyncImporter {
return function (uri, prev, done) {
static createImporter(name: string, importer: AsyncImporter): AsyncImporter {
return function (uri, prev, doneImporting) {
let importTimer = heimdall.start(`eyeglass:import:${name}`);
let done: typeof doneImporting = (data): void => {
importTimer.stop();
doneImporting(data);
};
uri = URI.web(uri);
prev = URI.system(prev);
importer.call(this, uri, prev, done);
Expand Down
2 changes: 1 addition & 1 deletion packages/eyeglass/src/importers/ModuleImporter.ts
Expand Up @@ -73,7 +73,7 @@ const ModuleImporter: ImporterFactory = function (eyeglass, sass, options, fallb
let includePaths = options.includePaths;
let root = options.eyeglass.root;

return ImportUtilities.createImporter(function(uri, prev, done) {
return ImportUtilities.createImporter("module", function(uri, prev, done) {
let importUtils = new ImportUtilities(eyeglass, sass, options, fallbackImporter, this);
let isRealFile = existsSync(prev);
// pattern to match moduleName/relativePath
Expand Down
124 changes: 73 additions & 51 deletions packages/eyeglass/src/modules/EyeglassModules.ts
Expand Up @@ -15,6 +15,7 @@ import { SassImplementation } from "../util/SassImplementation";
import { Dict, isPresent } from "../util/typescriptUtils";
import { EyeglassConfig } from "..";
import { Config } from "../util/Options";
import heimdall = require("heimdalljs");
// XXX For some weird reason importing ../Eyeglass to use the static VERSION constant doesn't work.
// XXX I get undefined from importing Eyeglass instead of the class I'm expecting.
// eslint-disable-next-line
Expand Down Expand Up @@ -77,61 +78,82 @@ export default class EyeglassModules {
config: Config;
private _modulePathMap: Dict<EyeglassModule> | undefined;
constructor(dir: string, config: EyeglassConfig, modules?: Array<ModuleSpecifier>) {
this.config = config;
let useGlobalModuleCache = config.eyeglass.useGlobalModuleCache;
this.issues = {
dependencies: {
versions: [],
missing: []
},
engine: {
missing: [],
incompatible: []
let timer = heimdall.start("eyeglass:modules");
try {
this.config = config;
let useGlobalModuleCache = config.eyeglass.useGlobalModuleCache;
this.issues = {
dependencies: {
versions: [],
missing: []
},
engine: {
missing: [],
incompatible: []
}
};

this.cache = {
access: new SimpleCache(),
modules: useGlobalModuleCache ? globalModuleCache : new SimpleCache<EyeglassModule>(),
modulePackage: useGlobalModuleCache ? globalModulePackageCache : new SimpleCache<string>(),
};

// find the nearest package.json for the given directory
dir = packageUtils.findNearestPackage(path.resolve(dir));

// resolve the current location into a module tree
let moduleTree = this.resolveModule(dir, true)!;

// if any modules were passed in, add them to the module tree
if (modules && modules.length) {
let discoverTimer = heimdall.start("eyeglass:modules:discovery");
try {
moduleTree.dependencies = modules.reduce((dependencies, mod) => {
let resolvedMod = new EyeglassModule(merge(mod, {
isEyeglassModule: true
}), this.discoverModules.bind(this));
dependencies[resolvedMod.name] = resolvedMod;
return dependencies;
}, moduleTree.dependencies);
} finally {
discoverTimer.stop();
}
}
};

this.cache = {
access: new SimpleCache(),
modules: useGlobalModuleCache ? globalModuleCache : new SimpleCache<EyeglassModule>(),
modulePackage: useGlobalModuleCache ? globalModulePackageCache : new SimpleCache<string>(),
};
let resolutionTimer = heimdall.start("eyeglass:modules:resolution");
try {
// convert the tree into a flat collection of deduped modules
let collection = this.dedupeModules(flattenModules(moduleTree));

// expose the collection
this.collection = collection;
// convert the collection object into a simple array for easy iteration
this.list = Object.keys(collection).map((name) => collection[name]!);
// prune and expose the tree
this.tree = this.pruneModuleTree(moduleTree);
// set the current projects name
this.projectName = moduleTree.name;
// expose a convenience reference to the eyeglass module itself
this.eyeglass = this.find("eyeglass")!;

// check for any issues we may have encountered
this.checkForIssues();
} catch (e) {
// typescript needs this catch & throw to convince it that the instance properties are initialized.
throw e;
} finally {
resolutionTimer.stop();
}

// find the nearest package.json for the given directory
dir = packageUtils.findNearestPackage(path.resolve(dir));

// resolve the current location into a module tree
let moduleTree = this.resolveModule(dir, true)!;

// if any modules were passed in, add them to the module tree
if (modules && modules.length) {
moduleTree.dependencies = modules.reduce((dependencies, mod) => {
let resolvedMod = new EyeglassModule(merge(mod, {
isEyeglassModule: true
}), this.discoverModules.bind(this));
dependencies[resolvedMod.name] = resolvedMod;
return dependencies;
}, moduleTree.dependencies);
/* istanbul ignore next - don't test debug */
debug.modules && debug.modules("discovered modules\n\t" + this.getGraph().replace(/\n/g, "\n\t"));
} catch (e) {
// typescript needs this catch & throw to convince it that the instance properties are initialized.
throw e;
} finally {
timer.stop();
}

// convert the tree into a flat collection of deduped modules
let collection = this.dedupeModules(flattenModules(moduleTree));

// expose the collection
this.collection = collection;
// convert the collection object into a simple array for easy iteration
this.list = Object.keys(collection).map((name) => collection[name]!);
// prune and expose the tree
this.tree = this.pruneModuleTree(moduleTree);
// set the current projects name
this.projectName = moduleTree.name;
// expose a convenience reference to the eyeglass module itself
this.eyeglass = this.find("eyeglass")!;

// check for any issues we may have encountered
this.checkForIssues();

/* istanbul ignore next - don't test debug */
debug.modules && debug.modules("discovered modules\n\t" + this.getGraph().replace(/\n/g, "\n\t"));
}

/**
Expand Down
23 changes: 23 additions & 0 deletions packages/eyeglass/src/types/heimdalljs.d.ts
@@ -0,0 +1,23 @@
interface Stats {
[k: string]: number;
}
interface StatsSchema<T> {
new (): T;
}
export class Cookie {
readonly stats: Stats;
stop(): void;
resume(): void;
}
export function start(name: string): Cookie;
export function node<Return, Context = undefined>(
name: string,
callback: (this: Context, stats: Stats) => Return | Promise<Return>,
context?: Context
): Promise<Return>;
export function node<Return, Schema extends object, Context = undefined>(
name: string,
schema: StatsSchema<Schema>,
callback: (this: Context, stats: Schema) => Return | Promise<Return>,
context?: Context
): Promise<Return>;
1 change: 1 addition & 0 deletions packages/eyeglass/tsconfig.json
Expand Up @@ -25,6 +25,7 @@
"target": "es5",
"sourceMap": true,
"paths": {
"*": ["types/*"],
"node-sass": ["../@types/node-sass"]
}
},
Expand Down

0 comments on commit 70577ca

Please sign in to comment.