Add sol-compiler watch mode
This commit is contained in:
parent
8ddf925a8f
commit
657b698e1e
@ -1,4 +1,17 @@
|
|||||||
[
|
[
|
||||||
|
{
|
||||||
|
"version": "2.0.0",
|
||||||
|
"changes": [
|
||||||
|
{
|
||||||
|
"note": "Add sol-compiler watch mode with -w flag",
|
||||||
|
"pr": "TODO"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"note": "Make error and warning colouring more visually pleasant and consistent with other compilers",
|
||||||
|
"pr": "TODO"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"version": "1.1.16",
|
"version": "1.1.16",
|
||||||
"changes": [
|
"changes": [
|
||||||
|
@ -44,7 +44,9 @@
|
|||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@0x/dev-utils": "^1.0.21",
|
"@0x/dev-utils": "^1.0.21",
|
||||||
"@0x/tslint-config": "^2.0.0",
|
"@0x/tslint-config": "^2.0.0",
|
||||||
|
"@types/chokidar": "^1.7.5",
|
||||||
"@types/mkdirp": "^0.5.2",
|
"@types/mkdirp": "^0.5.2",
|
||||||
|
"@types/pluralize": "^0.0.29",
|
||||||
"@types/require-from-string": "^1.2.0",
|
"@types/require-from-string": "^1.2.0",
|
||||||
"@types/semver": "^5.5.0",
|
"@types/semver": "^5.5.0",
|
||||||
"chai": "^4.0.1",
|
"chai": "^4.0.1",
|
||||||
@ -74,10 +76,12 @@
|
|||||||
"@0x/web3-wrapper": "^3.2.1",
|
"@0x/web3-wrapper": "^3.2.1",
|
||||||
"@types/yargs": "^11.0.0",
|
"@types/yargs": "^11.0.0",
|
||||||
"chalk": "^2.3.0",
|
"chalk": "^2.3.0",
|
||||||
|
"chokidar": "^2.0.4",
|
||||||
"ethereum-types": "^1.1.4",
|
"ethereum-types": "^1.1.4",
|
||||||
"ethereumjs-util": "^5.1.1",
|
"ethereumjs-util": "^5.1.1",
|
||||||
"lodash": "^4.17.5",
|
"lodash": "^4.17.5",
|
||||||
"mkdirp": "^0.5.1",
|
"mkdirp": "^0.5.1",
|
||||||
|
"pluralize": "^7.0.0",
|
||||||
"require-from-string": "^2.0.1",
|
"require-from-string": "^2.0.1",
|
||||||
"semver": "5.5.0",
|
"semver": "5.5.0",
|
||||||
"solc": "^0.4.23",
|
"solc": "^0.4.23",
|
||||||
|
@ -25,6 +25,10 @@ const SEPARATOR = ',';
|
|||||||
type: 'string',
|
type: 'string',
|
||||||
description: 'comma separated list of contracts to compile',
|
description: 'comma separated list of contracts to compile',
|
||||||
})
|
})
|
||||||
|
.option('watch', {
|
||||||
|
alias: 'w',
|
||||||
|
default: false,
|
||||||
|
})
|
||||||
.help().argv;
|
.help().argv;
|
||||||
const contracts = _.isUndefined(argv.contracts)
|
const contracts = _.isUndefined(argv.contracts)
|
||||||
? undefined
|
? undefined
|
||||||
@ -37,7 +41,11 @@ const SEPARATOR = ',';
|
|||||||
contracts,
|
contracts,
|
||||||
};
|
};
|
||||||
const compiler = new Compiler(opts);
|
const compiler = new Compiler(opts);
|
||||||
await compiler.compileAsync();
|
if (argv.watch) {
|
||||||
|
await compiler.watchAsync();
|
||||||
|
} else {
|
||||||
|
await compiler.compileAsync();
|
||||||
|
}
|
||||||
})().catch(err => {
|
})().catch(err => {
|
||||||
logUtils.log(err);
|
logUtils.log(err);
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
|
@ -6,13 +6,17 @@ import {
|
|||||||
NPMResolver,
|
NPMResolver,
|
||||||
RelativeFSResolver,
|
RelativeFSResolver,
|
||||||
Resolver,
|
Resolver,
|
||||||
|
SpyResolver,
|
||||||
URLResolver,
|
URLResolver,
|
||||||
} from '@0x/sol-resolver';
|
} from '@0x/sol-resolver';
|
||||||
import { logUtils } from '@0x/utils';
|
import { logUtils } from '@0x/utils';
|
||||||
|
import chalk from 'chalk';
|
||||||
|
import * as chokidar from 'chokidar';
|
||||||
import { CompilerOptions, ContractArtifact, ContractVersionData, StandardOutput } from 'ethereum-types';
|
import { CompilerOptions, ContractArtifact, ContractVersionData, StandardOutput } from 'ethereum-types';
|
||||||
import * as fs from 'fs';
|
import * as fs from 'fs';
|
||||||
import * as _ from 'lodash';
|
import * as _ from 'lodash';
|
||||||
import * as path from 'path';
|
import * as path from 'path';
|
||||||
|
import * as pluralize from 'pluralize';
|
||||||
import * as semver from 'semver';
|
import * as semver from 'semver';
|
||||||
import solc = require('solc');
|
import solc = require('solc');
|
||||||
|
|
||||||
@ -30,6 +34,7 @@ import {
|
|||||||
} from './utils/compiler';
|
} from './utils/compiler';
|
||||||
import { constants } from './utils/constants';
|
import { constants } from './utils/constants';
|
||||||
import { fsWrapper } from './utils/fs_wrapper';
|
import { fsWrapper } from './utils/fs_wrapper';
|
||||||
|
import { CompilationError } from './utils/types';
|
||||||
import { utils } from './utils/utils';
|
import { utils } from './utils/utils';
|
||||||
|
|
||||||
type TYPE_ALL_FILES_IDENTIFIER = '*';
|
type TYPE_ALL_FILES_IDENTIFIER = '*';
|
||||||
@ -129,6 +134,43 @@ export class Compiler {
|
|||||||
const promisedOutputs = this._compileContractsAsync(this._getContractNamesToCompile(), false);
|
const promisedOutputs = this._compileContractsAsync(this._getContractNamesToCompile(), false);
|
||||||
return promisedOutputs;
|
return promisedOutputs;
|
||||||
}
|
}
|
||||||
|
public async watchAsync(): Promise<void> {
|
||||||
|
console.clear(); // tslint:disable-line:no-console
|
||||||
|
logWithTime('Starting compilation in watch mode...');
|
||||||
|
const watcher = chokidar.watch('^$', { ignored: /(^|[\/\\])\../ });
|
||||||
|
const onFileChangedAsync = async () => {
|
||||||
|
watcher.unwatch('*'); // Stop watching
|
||||||
|
try {
|
||||||
|
await this.compileAsync();
|
||||||
|
logWithTime('Found 0 errors. Watching for file changes.');
|
||||||
|
} catch (err) {
|
||||||
|
if (err.typeName === 'CompilationError') {
|
||||||
|
logWithTime(`Found ${err.errorsCount} ${pluralize('error', err.errorsCount)}. Watching for file changes.`);
|
||||||
|
} else {
|
||||||
|
logWithTime('Found errors. Watching for file changes.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const pathsToWatch = this._getPathsToWatch();
|
||||||
|
watcher.add(pathsToWatch);
|
||||||
|
};
|
||||||
|
await onFileChangedAsync();
|
||||||
|
watcher.on('change', (changedFilePath: string) => {
|
||||||
|
console.clear(); // tslint:disable-line:no-console
|
||||||
|
logWithTime('File change detected. Starting incremental compilation...');
|
||||||
|
onFileChangedAsync();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
private _getPathsToWatch(): string[] {
|
||||||
|
const contractNames = this._getContractNamesToCompile();
|
||||||
|
const spyResolver = new SpyResolver(this._resolver);
|
||||||
|
for (const contractName of contractNames) {
|
||||||
|
const contractSource = spyResolver.resolve(contractName);
|
||||||
|
getSourceTreeHash(spyResolver, contractSource.path);
|
||||||
|
}
|
||||||
|
const pathsToWatch = _.uniq(spyResolver.resolvedContractSources.map(cs => cs.absolutePath));
|
||||||
|
return pathsToWatch;
|
||||||
|
}
|
||||||
private _getContractNamesToCompile(): string[] {
|
private _getContractNamesToCompile(): string[] {
|
||||||
let contractNamesToCompile;
|
let contractNamesToCompile;
|
||||||
if (this._specifiedContracts === ALL_CONTRACTS_IDENTIFIER) {
|
if (this._specifiedContracts === ALL_CONTRACTS_IDENTIFIER) {
|
||||||
@ -298,3 +340,7 @@ export class Compiler {
|
|||||||
logUtils.warn(`${contractName} artifact saved!`);
|
logUtils.warn(`${contractName} artifact saved!`);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function logWithTime(arg: string): void {
|
||||||
|
logUtils.log(`[${chalk.gray(new Date().toLocaleTimeString())}] ${arg}`);
|
||||||
|
}
|
||||||
|
@ -12,6 +12,7 @@ import { binPaths } from '../solc/bin_paths';
|
|||||||
|
|
||||||
import { constants } from './constants';
|
import { constants } from './constants';
|
||||||
import { fsWrapper } from './fs_wrapper';
|
import { fsWrapper } from './fs_wrapper';
|
||||||
|
import { CompilationError } from './types';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Gets contract data on network or returns if an artifact does not exist.
|
* Gets contract data on network or returns if an artifact does not exist.
|
||||||
@ -147,13 +148,13 @@ function printCompilationErrorsAndWarnings(solcErrors: solc.SolcError[]): void {
|
|||||||
if (!_.isEmpty(errors)) {
|
if (!_.isEmpty(errors)) {
|
||||||
errors.forEach(error => {
|
errors.forEach(error => {
|
||||||
const normalizedErrMsg = getNormalizedErrMsg(error.formattedMessage || error.message);
|
const normalizedErrMsg = getNormalizedErrMsg(error.formattedMessage || error.message);
|
||||||
logUtils.warn(chalk.red(normalizedErrMsg));
|
logUtils.log(chalk.red('error'), normalizedErrMsg);
|
||||||
});
|
});
|
||||||
throw new Error('Compilation errors encountered');
|
throw new CompilationError(errors.length);
|
||||||
} else {
|
} else {
|
||||||
warnings.forEach(warning => {
|
warnings.forEach(warning => {
|
||||||
const normalizedWarningMsg = getNormalizedErrMsg(warning.formattedMessage || warning.message);
|
const normalizedWarningMsg = getNormalizedErrMsg(warning.formattedMessage || warning.message);
|
||||||
logUtils.warn(chalk.yellow(normalizedWarningMsg));
|
logUtils.log(chalk.yellow('warning'), normalizedWarningMsg);
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -29,3 +29,12 @@ export interface Token {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export type DoneCallback = (err?: Error) => void;
|
export type DoneCallback = (err?: Error) => void;
|
||||||
|
|
||||||
|
export class CompilationError extends Error {
|
||||||
|
public errorsCount: number;
|
||||||
|
public typeName = 'CompilationError';
|
||||||
|
constructor(errorsCount: number) {
|
||||||
|
super('Compilation errors encountered');
|
||||||
|
this.errorsCount = errorsCount;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -52,7 +52,7 @@ describe('Compiler utils', () => {
|
|||||||
const source = await fsWrapper.readFileAsync(path, {
|
const source = await fsWrapper.readFileAsync(path, {
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
});
|
});
|
||||||
const dependencies = parseDependencies({ source, path });
|
const dependencies = parseDependencies({ source, path, absolutePath: path });
|
||||||
const expectedDependencies = [
|
const expectedDependencies = [
|
||||||
'zeppelin-solidity/contracts/token/ERC20/ERC20.sol',
|
'zeppelin-solidity/contracts/token/ERC20/ERC20.sol',
|
||||||
'packages/sol-compiler/lib/test/fixtures/contracts/TokenTransferProxy.sol',
|
'packages/sol-compiler/lib/test/fixtures/contracts/TokenTransferProxy.sol',
|
||||||
@ -68,7 +68,7 @@ describe('Compiler utils', () => {
|
|||||||
const source = await fsWrapper.readFileAsync(path, {
|
const source = await fsWrapper.readFileAsync(path, {
|
||||||
encoding: 'utf8',
|
encoding: 'utf8',
|
||||||
});
|
});
|
||||||
expect(parseDependencies({ source, path })).to.be.deep.equal([
|
expect(parseDependencies({ source, path, absolutePath: path })).to.be.deep.equal([
|
||||||
'zeppelin-solidity/contracts/ownership/Ownable.sol',
|
'zeppelin-solidity/contracts/ownership/Ownable.sol',
|
||||||
'zeppelin-solidity/contracts/token/ERC20/ERC20.sol',
|
'zeppelin-solidity/contracts/token/ERC20/ERC20.sol',
|
||||||
]);
|
]);
|
||||||
@ -77,7 +77,7 @@ describe('Compiler utils', () => {
|
|||||||
it.skip('correctly parses commented out dependencies', async () => {
|
it.skip('correctly parses commented out dependencies', async () => {
|
||||||
const path = '';
|
const path = '';
|
||||||
const source = `// import "./TokenTransferProxy.sol";`;
|
const source = `// import "./TokenTransferProxy.sol";`;
|
||||||
expect(parseDependencies({ path, source })).to.be.deep.equal([]);
|
expect(parseDependencies({ path, source, absolutePath: path })).to.be.deep.equal([]);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
Loading…
x
Reference in New Issue
Block a user