Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add a verbose and debug mode to parcel for better bug reports #1385

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/Asset.js
Expand Up @@ -149,6 +149,7 @@ class Asset {
}

async load() {
logger.verbose(`Loading ${this.basename} from the filesystem...`);
return await fs.readFile(this.name, this.encoding);
}

Expand Down Expand Up @@ -177,13 +178,19 @@ class Asset {
async process() {
if (!this.generated) {
await this.loadIfNeeded();
logger.verbose(`PreTransforming ${this.basename}...`);
await this.pretransform();
logger.verbose(`Getting dependencies of ${this.basename}...`);
await this.getDependencies();
logger.verbose(`Transforming ${this.basename}...`);
await this.transform();
logger.verbose(`Generating ${this.basename}...`);
this.generated = await this.generate();
this.hash = await this.generateHash();
}

logger.verbose(`${this.basename} processed`);

return this.generated;
}

Expand Down
5 changes: 5 additions & 0 deletions src/Bundle.js
@@ -1,5 +1,6 @@
const Path = require('path');
const crypto = require('crypto');
const logger = require('./Logger');

/**
* A Bundle represents an output file, containing multiple assets. Bundles can have
Expand Down Expand Up @@ -184,17 +185,21 @@ class Bundle {
async _package(bundler) {
let Packager = bundler.packagers.get(this.type);
let packager = new Packager(this, bundler);
let basename = Path.basename(this.name);

let startTime = Date.now();
await packager.setup();
await packager.start();
logger.verbose(`Started packaging ${basename}...`);

let included = new Set();
for (let asset of this.assets) {
logger.verbose(`Adding ${asset.basename} to ${basename}...`);
await this._addDeps(asset, packager, included);
}

await packager.end();
logger.verbose(`Finished packaging ${basename}...`);

this.bundleTime = Date.now() - startTime;
for (let asset of this.assets) {
Expand Down
71 changes: 71 additions & 0 deletions src/Bundler.js
Expand Up @@ -65,6 +65,14 @@ class Bundler extends EventEmitter {
this.rebuildTimeout = null;

logger.setOptions(this.options);

// Print all bundler options if parcel is in debug mode
if (this.options.logLevel > 4) {
logger.verbose(`Bundler options:`);
Object.keys(this.options).forEach(key => {
logger.verbose(` ${key}: ${JSON.stringify(this.options[key])}`);
});
}
}

normalizeEntries(entryFiles) {
Expand Down Expand Up @@ -190,6 +198,7 @@ class Bundler extends EventEmitter {
const pattern = /^(@.*\/)?parcel-plugin-.+/;
if (pattern.test(dep)) {
let plugin = await localRequire(dep, relative);
logger.verbose(`Load plugin: ${dep}...`);
await plugin(this);
}
}
Expand Down Expand Up @@ -232,49 +241,69 @@ class Bundler extends EventEmitter {
}
}

logger.verbose('[Bundler] Build the queued assets...');

// Build the queued assets.
let loadedAssets = await this.buildQueue.run();

// The changed assets are any that don't have a parent bundle yet
// plus the ones that were in the build queue.
let changedAssets = [...this.findOrphanAssets(), ...loadedAssets];

logger.verbose('[Bundler] Invalidating bundles...');

// Invalidate bundles
for (let asset of this.loadedAssets.values()) {
asset.invalidateBundle();
}

logger.verbose('[Bundler] bundles invalidated...');

logger.verbose('[Bundler] Create root bundle...');

// Create a root bundle to hold all of the entry assets, and add them to the tree.
this.mainBundle = new Bundle();
for (let asset of this.entryAssets) {
this.createBundleTree(asset, this.mainBundle);
}

logger.verbose('[Bundler] Root bundle created.');

// If there is only one child bundle, replace the root with that bundle.
if (this.mainBundle.childBundles.size === 1) {
this.mainBundle = Array.from(this.mainBundle.childBundles)[0];
}

logger.verbose('[Bundler] Generating bundle hash...');

// Generate the final bundle names, and replace references in the built assets.
this.bundleNameMap = this.mainBundle.getBundleNameMap(
this.options.contentHash
);

logger.verbose('[Bundler] Updating bundle name references...');

for (let asset of changedAssets) {
asset.replaceBundleNames(this.bundleNameMap);
}

logger.verbose('[Bundler] Emit hmr update...');

// Emit an HMR update if this is not the initial bundle.
if (this.hmr && !isInitialBundle) {
this.hmr.emitUpdate(changedAssets);
}

logger.verbose('[Bundler] Package everything up...');

// Package everything up
this.bundleHashes = await this.mainBundle.package(
this,
this.bundleHashes
);

logger.verbose('[Bundler] Unload any orphaned assets...');

// Unload any orphaned assets
this.unloadOrphanedAssets();

Expand Down Expand Up @@ -315,6 +344,8 @@ class Bundler extends EventEmitter {
return;
}

logger.verbose(`Start bundler...`);

await this.loadPlugins();
await loadEnv(Path.join(this.options.rootDir, 'index'));

Expand Down Expand Up @@ -379,6 +410,7 @@ class Bundler extends EventEmitter {
}

if (!this.watchedAssets.has(path)) {
logger.verbose(`Add ${path} to filewatcher...`);
this.watcher.watch(path);
this.watchedAssets.set(path, new Set());
}
Expand All @@ -395,6 +427,7 @@ class Bundler extends EventEmitter {
watched.delete(asset);

if (watched.size === 0) {
logger.verbose(`Remove ${path} from filewatcher...`);
this.watchedAssets.delete(path);
this.watcher.unwatch(path);
}
Expand Down Expand Up @@ -472,6 +505,8 @@ class Bundler extends EventEmitter {
}

async processAsset(asset, isRebuild) {
logger.verbose(`${asset.relativeName} - Start processing`);

if (isRebuild) {
asset.invalidate();
if (this.cache) {
Expand All @@ -480,15 +515,20 @@ class Bundler extends EventEmitter {
}

await this.loadAsset(asset);

logger.verbose(`${asset.relativeName} - Done processing`);
}

async loadAsset(asset) {
if (asset.processed) {
return;
}

logger.verbose(`${asset.relativeName} - Load asset`);

if (!this.errored) {
logger.status(emoji.progress, `Building ${asset.basename}...`);
logger.verbose(`${asset.relativeName} - Queuing`);
}

// Mark the asset processed so we don't load it twice
Expand All @@ -499,14 +539,21 @@ class Bundler extends EventEmitter {
let processed = this.cache && (await this.cache.read(asset.name));
let cacheMiss = false;
if (!processed || asset.shouldInvalidate(processed.cacheData)) {
logger.verbose(
`${asset.relativeName} - ${!processed ? 'Cache miss' : 'Invalidated'}`
);
processed = await this.farm.run(asset.name);
cacheMiss = true;
} else {
logger.verbose(`${asset.relativeName} - Copied from cache`);
}

asset.buildTime = Date.now() - startTime;
asset.generated = processed.generated;
asset.hash = processed.hash;

logger.verbose(`${asset.relativeName} - Finished building`);

// Call the delegate to get implicit dependencies
let dependencies = processed.dependencies;
if (this.delegate.getImplicitDependencies) {
Expand All @@ -519,6 +566,12 @@ class Bundler extends EventEmitter {
// Resolve and load asset dependencies
let assetDeps = await Promise.all(
dependencies.map(async dep => {
logger.verbose(
`${asset.relativeName} - Getting dependency: ${Path.relative(
this.options.rootDir,
dep.name
)}`
);
if (dep.includedInParent) {
// This dependency is already included in the parent's generated output,
// so no need to load it. We map the name back to the parent asset so
Expand All @@ -527,9 +580,20 @@ class Bundler extends EventEmitter {
} else {
let assetDep = await this.resolveDep(asset, dep);
if (assetDep) {
logger.verbose(
`${asset.relativeName} - Load dependency: ${
assetDep.relativeName
}`
);
await this.loadAsset(assetDep);
}

logger.verbose(
`${asset.relativeName} - Done loading dependency: ${
assetDep.relativeName
}`
);

return assetDep;
}
})
Expand All @@ -545,9 +609,15 @@ class Bundler extends EventEmitter {
}
});

logger.verbose(`${asset.relativeName} - Dependencies loaded`);

if (this.cache && cacheMiss) {
this.cache.write(asset.name, processed);

logger.verbose(`${asset.relativeName} - Cache written`);
}

logger.verbose(`${asset.relativeName} - Done loading asset`);
}

createBundleTree(asset, bundle, dep, parentBundles = new Set()) {
Expand Down Expand Up @@ -685,6 +755,7 @@ class Bundler extends EventEmitter {
}

logger.clear();
logger.verbose(`Detected a change in ${path}...`);
logger.status(emoji.progress, `Building ${Path.basename(path)}...`);

// Add the asset to the rebuild queue, and reset the timeout.
Expand Down
45 changes: 40 additions & 5 deletions src/Logger.js
Expand Up @@ -4,6 +4,9 @@ const prettyError = require('./utils/prettyError');
const emoji = require('./utils/emoji');
const {countBreaks} = require('grapheme-breaker');
const stripAnsi = require('strip-ansi');
const promisify = require('./utils/promisify');
const path = require('path');
const fs = require('fs');

class Logger {
constructor(options) {
Expand Down Expand Up @@ -44,13 +47,42 @@ class Logger {
}

write(message, persistent = false) {
if (this.logLevel > 3) {
return this.verbose(message);
}

if (!persistent) {
this.lines += this.countLines(message);
}

this._log(message);
}

verbose(message) {
if (this.logLevel < 4) {
return;
}

let currDate = new Date();
message = `[${currDate.toLocaleTimeString()}]: ${message}`;

if (this.logLevel > 4) {
if (!this.logFile) {
this.logFile = fs.createWriteStream(
path.join(
process.cwd(),
`parcel-debug-${currDate.toLocaleDateString()}@${currDate.toLocaleTimeString()}.log`
)
);
this.logFile.write = promisify(this.logFile.write.bind(this.logFile));
}

this.logFile.write(stripAnsi(message) + '\n');
}

this._log(message);
}

log(message) {
if (this.logLevel < 3) {
return;
Expand Down Expand Up @@ -93,7 +125,7 @@ class Logger {
}

clear() {
if (!this.color || this.isTest) {
if (!this.color || this.isTest || this.logLevel > 3) {
return;
}

Expand Down Expand Up @@ -127,15 +159,18 @@ class Logger {
return;
}

let msgContent = this.chalk[color].bold(`${emoji} ${message}`);

if (this.logLevel > 3) {
return this.verbose(msgContent);
}

let hasStatusLine = this.statusLine != null;
if (!hasStatusLine) {
this.statusLine = this.lines;
}

this.writeLine(
this.statusLine,
this.chalk[color].bold(`${emoji} ${message}`)
);
this.writeLine(this.statusLine, msgContent);

if (!hasStatusLine) {
process.stdout.write('\n');
Expand Down
6 changes: 3 additions & 3 deletions src/cli.js
Expand Up @@ -55,7 +55,7 @@ program
.option(
'--log-level <level>',
'set the log level, either "0" (no output), "1" (errors), "2" (warnings + errors) or "3" (all).',
/^([0-3])$/
/^([0-5])$/
)
.action(bundle);

Expand Down Expand Up @@ -99,7 +99,7 @@ program
.option(
'--log-level <level>',
'set the log level, either "0" (no output), "1" (errors), "2" (warnings + errors) or "3" (all).',
/^([0-3])$/
/^([0-5])$/
)
.action(bundle);

Expand Down Expand Up @@ -134,7 +134,7 @@ program
.option(
'--log-level <level>',
'set the log level, either "0" (no output), "1" (errors), "2" (warnings + errors) or "3" (all).',
/^([0-3])$/
/^([0-5])$/
)
.action(bundle);

Expand Down