diff --git a/.editorconfig b/.editorconfig
new file mode 100644
index 0000000..7ab7fe3
--- /dev/null
+++ b/.editorconfig
@@ -0,0 +1,26 @@
+root = true
+
+[*]
+indent_style = space
+indent_size = 4
+charset = utf-8
+trim_trailing_whitespace = true
+insert_final_newline = true
+max_line_length = 233
+
+[*.json]
+indent_style = space
+indent_size = 2
+
+[*.ejs]
+insert_final_newline = false
+
+[*.yml]
+indent_style = space
+indent_size = 2
+
+[test/cases/parsing/bom/bomfile.{css,js}]
+charset = utf-8-bom
+
+[*.md]
+trim_trailing_whitespace = false
diff --git a/.gitattributes b/.gitattributes
index dd6c9a6..03e0809 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1,3 +1,22 @@
+# Automatically normalize line endings for all text-based files
+# http://git-scm.com/docs/gitattributes#_end_of_line_conversion
* text=auto
+
+# For the following file types, normalize line endings to LF on
+# checkin and prevent conversion to CRLF when they are checked out
+# (this is required in order to prevent newline related issues like,
+# for example, after the build script is run)
+.* text eol=lf
+*.css text eol=lf
+*.html text eol=lf
+*.js text eol=lf
+*.ts text eol=lf
+*.json text eol=lf
+*.md text eol=lf
+*.sh text eol=lf
+*.txt text eol=lf
+*.xml text eol=lf
+
+test/statsCases/* eol=lf
examples/* eol=lf
-bin/* eol=lf
\ No newline at end of file
+bin/* eol=lf
diff --git a/.gitignore b/.gitignore
index 017a83d..7ae5b39 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,5 +1,56 @@
-/node_modules
-/test/fixtures
-/playground/folder
+# Logs
+logs
+*.log
+npm-debug.log*
+
+# Runtime data
+pids
+*.pid
+*.seed
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
/coverage
-.DS_Store
\ No newline at end of file
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (http://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+/node_modules
+jspm_packages
+
+# Optional npm cache directory
+.npm
+
+# Optional REPL history
+.node_repl_history
+
+# tests
+/test/js
+/test/browsertest/js
+
+# benchmark
+/benchmark/js
+/benchmark/fixtures
+
+# custom
+/examples/*/js
+lib/**/*.map
+
+# IDE
+.DS_Store
+.idea
+.vscode
+
+yarn.lock
\ No newline at end of file
diff --git a/lib/DirectoryWatcher.ts b/lib/DirectoryWatcher.ts
new file mode 100644
index 0000000..804aade
--- /dev/null
+++ b/lib/DirectoryWatcher.ts
@@ -0,0 +1,372 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+ */
+import { EventEmitter } from 'events';
+import async = require('async');
+import chokidar = require('chokidar');
+import fs = require('graceful-fs');
+import path = require('path');
+import watcherManager = require('./watcherManager');
+import Watcher = require('./Watcher')
+import Watchpack = require('./watchpack')
+
+let FS_ACCURENCY = 10000;
+
+function withoutCase(str: string) {
+ return str.toLowerCase();
+}
+
+class DirectoryWatcher extends EventEmitter {
+ directories: {
+ [path: string]: Watcher | true
+ }
+ files: {
+ [path: string]: [number, number]
+ }
+ initialScan: boolean
+ initialScanRemoved: string[]
+ nestedWatching: boolean
+ path: string
+ refs: number
+ watcher: fs.FSWatcher
+ watchers: {
+ [path: string]: Watcher[]
+ }
+
+ constructor(directoryPath: string, public options: Watchpack.WatcherOptions) {
+ super();
+ this.path = directoryPath;
+ this.files = {};
+ this.directories = {};
+ this.watcher = chokidar.watch(directoryPath, {
+ ignoreInitial: true,
+ persistent: true,
+ followSymlinks: false,
+ depth: 0,
+ atomic: false,
+ alwaysStat: true,
+ ignorePermissionErrors: true,
+ ignored: options.ignored,
+ usePolling: options.poll ? true : undefined,
+ interval: typeof options.poll === 'number' ? options.poll : undefined
+ });
+ this.watcher.on('add', this.onFileAdded.bind(this));
+ this.watcher.on('addDir', this.onDirectoryAdded.bind(this));
+ this.watcher.on('change', this.onChange.bind(this));
+ this.watcher.on('unlink', this.onFileUnlinked.bind(this));
+ this.watcher.on('unlinkDir', this.onDirectoryUnlinked.bind(this));
+ this.watcher.on('error', this.onWatcherError.bind(this));
+ this.initialScan = true;
+ this.nestedWatching = false;
+ this.initialScanRemoved = [];
+ this.doInitialScan();
+ this.watchers = {};
+ this.refs = 0;
+ }
+
+ setFileTime(filePath: string, mtime: number, initial: boolean, type?: string | boolean) {
+ const now = Date.now();
+ const old = this.files[filePath];
+
+ this.files[filePath] = [initial ? Math.min(now, mtime) : now, mtime];
+
+ // we add the fs accurency to reach the maximum possible mtime
+ mtime = mtime + FS_ACCURENCY;
+
+ if (!old) {
+ if (mtime) {
+ if (this.watchers[withoutCase(filePath)]) {
+ this.watchers[withoutCase(filePath)].forEach(w => {
+ if (!initial || w.checkStartTime(mtime, initial)) {
+ w.emit('change', mtime);
+ }
+ });
+ }
+ }
+ }
+ else if (!initial && mtime && type !== 'add') {
+ if (this.watchers[withoutCase(filePath)]) {
+ this.watchers[withoutCase(filePath)].forEach(w => {
+ w.emit('change', mtime);
+ });
+ }
+ }
+ else if (!initial && !mtime) {
+ delete this.files[filePath];
+ if (this.watchers[withoutCase(filePath)]) {
+ this.watchers[withoutCase(filePath)].forEach(w => {
+ w.emit('remove');
+ });
+ }
+ }
+ if (this.watchers[withoutCase(this.path)]) {
+ this.watchers[withoutCase(this.path)].forEach(w => {
+ if (!initial || w.checkStartTime(mtime, initial)) {
+ w.emit('change', filePath, mtime);
+ }
+ });
+ }
+ }
+
+ setDirectory(directoryPath: string, exist: boolean, initial: boolean) {
+ const old = this.directories[directoryPath];
+ if (!old) {
+ if (exist) {
+ if (this.nestedWatching) {
+ this.createNestedWatcher(directoryPath);
+ }
+ else {
+ this.directories[directoryPath] = true;
+ }
+ }
+ }
+ else {
+ if (!exist) {
+ if (this.nestedWatching) {
+ (this.directories[directoryPath] as Watcher).close();
+ }
+ delete this.directories[directoryPath];
+ if (!initial && this.watchers[withoutCase(this.path)]) {
+ this.watchers[withoutCase(this.path)].forEach(w => {
+ w.emit('change', directoryPath, w.data);
+ });
+ }
+ }
+ }
+ }
+
+ createNestedWatcher(directoryPath: string) {
+ this.directories[directoryPath] = watcherManager.watchDirectory(directoryPath, this.options, 1);
+ (this.directories[directoryPath] as Watcher).on('change', (filePath: string, mtime: number) => {
+ if (this.watchers[withoutCase(this.path)]) {
+ this.watchers[withoutCase(this.path)].forEach(w => {
+ if (w.checkStartTime(mtime, false)) {
+ w.emit('change', filePath, mtime);
+ }
+ });
+ }
+ });
+ }
+
+ setNestedWatching(flag: boolean) {
+ if (this.nestedWatching !== !!flag) {
+ this.nestedWatching = !!flag;
+ if (this.nestedWatching) {
+ Object.keys(this.directories).forEach(function (directory) {
+ this.createNestedWatcher(directory);
+ }, this);
+ }
+ else {
+ Object.keys(this.directories).forEach(function (directory) {
+ this.directories[directory].close();
+ this.directories[directory] = true;
+ }, this);
+ }
+ }
+ }
+
+ watch(filePath: string, startTime: number) {
+ this.watchers[withoutCase(filePath)] = this.watchers[withoutCase(filePath)] || [];
+ this.refs++;
+ const watcher = new Watcher(this, filePath, startTime);
+ watcher.on('closed', () => {
+ const idx = this.watchers[withoutCase(filePath)].indexOf(watcher);
+ this.watchers[withoutCase(filePath)].splice(idx, 1);
+ if (this.watchers[withoutCase(filePath)].length === 0) {
+ delete this.watchers[withoutCase(filePath)];
+ if (this.path === filePath) {
+ this.setNestedWatching(false);
+ }
+ }
+ if (--this.refs <= 0) {
+ this.close();
+ }
+ });
+ this.watchers[withoutCase(filePath)].push(watcher);
+ let data: [number, number] | boolean;
+ if (filePath === this.path) {
+ this.setNestedWatching(true);
+ data = false;
+ Object.keys(this.files).forEach(function (file) {
+ const d = this.files[file];
+ if (!data) {
+ data = d;
+ }
+ else {
+ data = [Math.max(data[0], d[0]), Math.max(data[1], d[1])];
+ }
+ }, this);
+ }
+ else {
+ data = this.files[filePath];
+ }
+ process.nextTick(() => {
+ if (data) {
+ const ts = data[0] === data[1] ? data[0] + FS_ACCURENCY : data[0];
+ if (ts > startTime) {
+ watcher.emit('change', data[1] + FS_ACCURENCY);
+ }
+ }
+ else if (this.initialScan && this.initialScanRemoved.includes(filePath)) {
+ watcher.emit('remove');
+ }
+ });
+ return watcher;
+ }
+
+ onFileAdded(filePath: string, stat: fs.Stats) {
+ if (filePath.indexOf(this.path) !== 0) {
+ return;
+ }
+ if (/[\\\/]/.test(filePath.substr(this.path.length + 1))) {
+ return;
+ }
+
+ this.setFileTime(filePath, +stat.mtime, false, 'add');
+ }
+
+ onDirectoryAdded(directoryPath: string /*, stat */) {
+ if (directoryPath.indexOf(this.path) !== 0) {
+ return;
+ }
+ if (/[\\\/]/.test(directoryPath.substr(this.path.length + 1))) {
+ return;
+ }
+ this.setDirectory(directoryPath, true, false);
+ }
+
+ onChange(filePath: string, stat: fs.Stats) {
+ if (filePath.indexOf(this.path) !== 0) {
+ return;
+ }
+ if (/[\\\/]/.test(filePath.substr(this.path.length + 1))) {
+ return;
+ }
+ const mtime = +stat.mtime;
+ if (FS_ACCURENCY > 1 && mtime % 1 !== 0) {
+ FS_ACCURENCY = 1;
+ }
+ else if (FS_ACCURENCY > 10 && mtime % 10 !== 0) {
+ FS_ACCURENCY = 10;
+ }
+ else if (FS_ACCURENCY > 100 && mtime % 100 !== 0) {
+ FS_ACCURENCY = 100;
+ }
+ else if (FS_ACCURENCY > 1000 && mtime % 1000 !== 0) {
+ FS_ACCURENCY = 1000;
+ }
+ else if (FS_ACCURENCY > 2000 && mtime % 2000 !== 0) {
+ FS_ACCURENCY = 2000;
+ }
+ this.setFileTime(filePath, mtime, false, 'change');
+ }
+
+ onFileUnlinked(filePath: string) {
+ if (filePath.indexOf(this.path) !== 0) {
+ return;
+ }
+ if (/[\\\/]/.test(filePath.substr(this.path.length + 1))) {
+ return;
+ }
+ this.setFileTime(filePath, null, false, 'unlink');
+ if (this.initialScan) {
+ this.initialScanRemoved.push(filePath);
+ }
+ }
+
+ onDirectoryUnlinked(directoryPath: string) {
+ if (directoryPath.indexOf(this.path) !== 0) {
+ return;
+ }
+ if (/[\\\/]/.test(directoryPath.substr(this.path.length + 1))) {
+ return;
+ }
+ this.setDirectory(directoryPath, false, false);
+ if (this.initialScan) {
+ this.initialScanRemoved.push(directoryPath);
+ }
+ }
+
+ onWatcherError() /* err */ {}
+
+ doInitialScan() {
+ fs.readdir(this.path, (err, items) => {
+ if (err) {
+ this.initialScan = false;
+ return;
+ }
+ async.forEach(items, (item, callback) => {
+ const itemPath = path.join(this.path, item);
+ fs.stat(itemPath, (err2, stat) => {
+ if (!this.initialScan) {
+ return;
+ }
+ if (err2) {
+ callback();
+ return;
+ }
+ if (stat.isFile()) {
+ if (!this.files[itemPath]) {
+ this.setFileTime(itemPath, +stat.mtime, true);
+ }
+ }
+ else if (stat.isDirectory()) {
+ if (!this.directories[itemPath]) {
+ this.setDirectory(itemPath, true, true);
+ }
+ }
+ callback();
+ });
+ }, () => {
+ this.initialScan = false;
+ this.initialScanRemoved = null;
+ });
+ });
+ }
+
+ getTimes(): {
+ [path: string]: number;
+ } {
+ const obj = {};
+ let selfTime = 0;
+ Object.keys(this.files).forEach(function (file) {
+ const data = this.files[file];
+ if (data[1]) {
+ const time = Math.max(data[0], data[1] + FS_ACCURENCY);
+ obj[file] = time;
+ if (time > selfTime) {
+ selfTime = time;
+ }
+ }
+ }, this);
+ if (this.nestedWatching) {
+ Object.keys(this.directories).forEach(function (dir) {
+ const w = this.directories[dir];
+ const times = w.directoryWatcher.getTimes();
+ Object.keys(times).forEach(file => {
+ const time = times[file];
+ obj[file] = time;
+ if (time > selfTime) {
+ selfTime = time;
+ }
+ });
+ }, this);
+ obj[this.path] = selfTime;
+ }
+ return obj;
+ }
+
+ close() {
+ this.initialScan = false;
+ this.watcher.close();
+ if (this.nestedWatching) {
+ Object.keys(this.directories).forEach(function (dir) {
+ this.directories[dir].close();
+ }, this);
+ }
+ this.emit('closed');
+ }
+}
+
+export = DirectoryWatcher;
diff --git a/lib/Watcher.ts b/lib/Watcher.ts
new file mode 100644
index 0000000..ad7d43e
--- /dev/null
+++ b/lib/Watcher.ts
@@ -0,0 +1,31 @@
+import { EventEmitter } from 'events'
+import DirectoryWatcher = require('./DirectoryWatcher')
+
+class Watcher extends EventEmitter {
+ data: number
+ directoryWatcher: DirectoryWatcher
+ path: string
+ startTime: number
+
+ constructor(directoryWatcher: DirectoryWatcher, filePath: string, startTime: number) {
+ super();
+ this.directoryWatcher = directoryWatcher;
+ this.path = filePath;
+ this.startTime = startTime && +startTime;
+ this.data = 0;
+ }
+
+ checkStartTime(mtime: number, initial: boolean) {
+ if (typeof this.startTime !== 'number') {
+ return !initial;
+ }
+ const startTime = this.startTime;
+ return startTime <= mtime;
+ }
+
+ close() {
+ this.emit('closed');
+ }
+}
+
+export = Watcher
diff --git a/lib/watcherManager.ts b/lib/watcherManager.ts
new file mode 100644
index 0000000..c158c95
--- /dev/null
+++ b/lib/watcherManager.ts
@@ -0,0 +1,41 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+ */
+import path = require('path');
+import DirectoryWatcher = require('./DirectoryWatcher')
+import Watchpack = require('./watchpack')
+
+class WatcherManager {
+ directoryWatchers: {
+ [key: string]: DirectoryWatcher
+ }
+
+ constructor() {
+ this.directoryWatchers = {};
+ }
+
+ getDirectoryWatcher(directory: string, options: Watchpack.WatcherOptions) {
+ const DirectoryWatcher = require('./DirectoryWatcher');
+ options = options || {};
+ const key = `${directory} ${JSON.stringify(options)}`;
+ if (!this.directoryWatchers[key]) {
+ this.directoryWatchers[key] = new DirectoryWatcher(directory, options);
+ this.directoryWatchers[key].on('closed', () => {
+ delete this.directoryWatchers[key];
+ });
+ }
+ return this.directoryWatchers[key];
+ }
+
+ watchFile(p: string, options: Watchpack.WatcherOptions, startTime: number) {
+ const directory = path.dirname(p);
+ return this.getDirectoryWatcher(directory, options).watch(p, startTime);
+ }
+
+ watchDirectory(directory: string, options: Watchpack.WatcherOptions, startTime: number) {
+ return this.getDirectoryWatcher(directory, options).watch(directory, startTime);
+ }
+}
+
+export = new WatcherManager();
diff --git a/lib/watchpack.ts b/lib/watchpack.ts
new file mode 100644
index 0000000..eae18c4
--- /dev/null
+++ b/lib/watchpack.ts
@@ -0,0 +1,160 @@
+/*
+ MIT License http://www.opensource.org/licenses/mit-license.php
+ Author Tobias Koppers @sokra
+ */
+import watcherManager = require('./watcherManager');
+
+import { EventEmitter } from 'events'
+import Watcher = require('./Watcher')
+import DirectoryWatcher = require('./DirectoryWatcher')
+
+class Watchpack extends EventEmitter {
+ aggregatedChanges: string[]
+ aggregateTimeout: NodeJS.Timer // setTimeout id
+ dirWatchers: Watcher[]
+ fileWatchers: Watcher[]
+ mtimes: {
+ [path: string]: number
+ }
+ options: Watchpack.WatchOptions
+ paused: boolean
+ watcherOptions: Watchpack.WatcherOptions
+
+ constructor(options: Watchpack.WatchOptions) {
+ super();
+ if (!options) {
+ options = {} as any;
+ }
+ if (!options.aggregateTimeout) {
+ options.aggregateTimeout = 200;
+ }
+ this.options = options;
+ this.watcherOptions = {
+ ignored: options.ignored,
+ poll: options.poll
+ };
+ this.fileWatchers = [];
+ this.dirWatchers = [];
+ this.mtimes = {};
+ this.paused = false;
+ this.aggregatedChanges = [];
+ this.aggregateTimeout = null;
+ this._onTimeout = this._onTimeout.bind(this);
+ }
+
+ watch(files: string[], directories: string[], startTime: number) {
+ this.paused = false;
+ const oldFileWatchers = this.fileWatchers;
+ const oldDirWatchers = this.dirWatchers;
+ this.fileWatchers = files.map(function (file) {
+ return this._fileWatcher(file, watcherManager.watchFile(file, this.watcherOptions, startTime));
+ }, this);
+ this.dirWatchers = directories.map(function (dir) {
+ return this._dirWatcher(dir, watcherManager.watchDirectory(dir, this.watcherOptions, startTime));
+ }, this);
+ oldFileWatchers.forEach(w => {
+ w.close();
+ }, this);
+ oldDirWatchers.forEach(w => {
+ w.close();
+ }, this);
+ }
+
+ close() {
+ this.paused = true;
+ if (this.aggregateTimeout) {
+ clearTimeout(this.aggregateTimeout);
+ }
+ this.fileWatchers.forEach(w => {
+ w.close();
+ }, this);
+ this.dirWatchers.forEach(w => {
+ w.close();
+ }, this);
+ this.fileWatchers.length = 0;
+ this.dirWatchers.length = 0;
+ }
+
+ pause() {
+ this.paused = true;
+ if (this.aggregateTimeout) {
+ clearTimeout(this.aggregateTimeout);
+ }
+ }
+
+ getTimes(): {
+ [path: string]: number;
+ } {
+ const directoryWatchers: DirectoryWatcher[] = [];
+ addWatchersToArray(this.fileWatchers.concat(this.dirWatchers), directoryWatchers);
+ const obj = {};
+ directoryWatchers.forEach(w => {
+ const times = w.getTimes();
+ Object.keys(times).forEach(file => {
+ obj[file] = times[file];
+ });
+ });
+ return obj;
+ }
+
+ _fileWatcher(file: string, watcher: Watcher) {
+ watcher.on('change', this._onChange.bind(this, file));
+ return watcher;
+ }
+
+ _dirWatcher(item: string, watcher: Watcher) {
+ watcher.on('change', (file: string, mtime: number) => {
+ this._onChange(item, mtime, file);
+ });
+ return watcher;
+ }
+
+ _onChange(item: string, mtime: number, file = item) {
+ this.mtimes[file] = mtime;
+ if (this.paused) {
+ return;
+ }
+ this.emit('change', file, mtime);
+ if (this.aggregateTimeout) {
+ clearTimeout(this.aggregateTimeout);
+ }
+ if (!this.aggregatedChanges.includes(item)) {
+ this.aggregatedChanges.push(item);
+ }
+ this.aggregateTimeout = setTimeout(this._onTimeout, this.options.aggregateTimeout);
+ }
+
+ _onTimeout() {
+ this.aggregateTimeout = null;
+ const changes = this.aggregatedChanges;
+ this.aggregatedChanges = [];
+ this.emit('aggregated', changes);
+ }
+}
+
+declare namespace Watchpack {
+ interface WatcherOptions {
+ ignored?: string[] | string | RegExp | ((path: string)=> boolean)
+ poll?: boolean | number
+ }
+
+ interface WatchOptions extends WatcherOptions {
+ aggregateTimeout?: number
+ }
+}
+
+export = Watchpack;
+
+function addWatchersToArray(watchers: Watcher[], array: any[]) {
+ watchers.forEach(w => {
+ if (!array.includes(w.directoryWatcher)) {
+ array.push(w.directoryWatcher);
+ addWatchersToArray(Object.keys(w.directoryWatcher.directories).reduce((a, dir) => {
+ if (w.directoryWatcher.directories[dir] !== true) {
+ a.push(w.directoryWatcher.directories[dir]);
+ }
+ return a;
+ }, []), array);
+ }
+ });
+}
diff --git a/package.json b/package.json
index 7aec936..d932d69 100644
--- a/package.json
+++ b/package.json
@@ -15,7 +15,8 @@
"travis": "npm run cover -- --report lcovonly",
"lint": "eslint lib",
"precover": "npm run lint",
- "cover": "istanbul cover node_modules/mocha/bin/_mocha"
+ "cover": "istanbul cover node_modules/mocha/bin/_mocha",
+ "build": "tsc || true"
},
"repository": {
"type": "git",
@@ -33,9 +34,15 @@
"istanbul": "^0.4.3",
"mocha": "^2.1.0",
"rimraf": "^2.2.8",
- "should": "^8.3.1"
+ "should": "^8.3.1",
+ "tslint": "^4.0.0",
+ "typescript": "^2.0.0"
},
"dependencies": {
+ "@types/async": "^2.0.34",
+ "@types/chokidar": "^1.4.29",
+ "@types/graceful-fs": "^2.0.29",
+ "@types/node": "^0.0.2",
"async": "^2.1.2",
"chokidar": "^1.4.3",
"graceful-fs": "^4.1.2"
diff --git a/tsconfig.json b/tsconfig.json
new file mode 100644
index 0000000..8dce7c8
--- /dev/null
+++ b/tsconfig.json
@@ -0,0 +1,24 @@
+{
+ "compileOnSave": true,
+ "compilerOptions": {
+ "module": "commonjs",
+ "target": "es5",
+ "noEmit": false,
+ "sourceMap": true,
+ "lib": [
+ "es2016",
+ "dom"
+ ],
+ "declaration": true,
+ "declarationDir": "./typings",
+ "noImplicitAny": true,
+ "noImplicitThis": false,
+ "suppressImplicitAnyIndexErrors": true,
+ "pretty": true,
+ "noImplicitReturns": false,
+ "strictNullChecks": false
+ },
+ "exclude": [
+ "node_modules"
+ ]
+}
diff --git a/tslint.json b/tslint.json
new file mode 100644
index 0000000..057bbba
--- /dev/null
+++ b/tslint.json
@@ -0,0 +1,71 @@
+{
+ "rules": {
+ "class-name": true,
+ "comment-format": [
+ true,
+ "check-space"
+ ],
+ "curly": true,
+ "eofline": true,
+ "forin": true,
+ "indent": [
+ true,
+ "spaces"
+ ],
+ "label-position": true,
+ "linebreak-style": [
+ true,
+ "LF"
+ ],
+ "no-arg": true,
+ "no-construct": true,
+ "no-debugger": true,
+ "no-duplicate-variable": true,
+ "no-empty": false,
+ "no-eval": true,
+ "no-inferrable-types": true,
+ "no-internal-module": true,
+ "no-trailing-whitespace": true,
+ "no-unsafe-finally": true,
+ "no-unused-expression": true,
+ "no-unused-variable": true,
+ "no-var-keyword": true,
+ "one-line": [
+ true,
+ "check-open-brace",
+ "check-whitespace"
+ ],
+ "quotemark": [
+ true,
+ "single"
+ ],
+ "semicolon": false,
+ "triple-equals": [
+ true,
+ "allow-null-check"
+ ],
+ "typedef-whitespace": [
+ true,
+ {
+ "call-signature": "nospace",
+ "index-signature": "nospace",
+ "parameter": "nospace",
+ "property-declaration": "nospace",
+ "variable-declaration": "nospace"
+ }
+ ],
+ "variable-name": [
+ true,
+ "ban-keywords"
+ ],
+ "whitespace": [
+ true,
+ "check-branch",
+ "check-decl",
+ "check-operator",
+ "check-module",
+ "check-separator",
+ "check-type"
+ ]
+ }
+}
diff --git a/typings/DirectoryWatcher.d.ts b/typings/DirectoryWatcher.d.ts
new file mode 100644
index 0000000..f348e80
--- /dev/null
+++ b/typings/DirectoryWatcher.d.ts
@@ -0,0 +1,58 @@
+///
+///
+import { EventEmitter } from 'events'
+import fs = require('graceful-fs');
+import Watcher = require('./Watcher');
+import Watchpack = require('./watchpack');
+
+declare class DirectoryWatcher extends EventEmitter {
+ options: Watchpack.WatcherOptions;
+ directories: {
+ [path: string]: Watcher | true;
+ };
+ files: {
+ [path: string]: [number, number];
+ };
+ initialScan: boolean;
+ initialScanRemoved: string[];
+ nestedWatching: boolean;
+ path: string;
+ refs: number;
+ watcher: fs.FSWatcher;
+ watchers: {
+ [path: string]: Watcher[];
+ };
+
+ constructor(directoryPath: string, options: Watchpack.WatcherOptions);
+
+ setFileTime(filePath: string, mtime: number, initial: boolean, type?: string | boolean): void;
+
+ setDirectory(directoryPath: string, exist: boolean, initial: boolean): void;
+
+ createNestedWatcher(directoryPath: string): void;
+
+ setNestedWatching(flag: boolean): void;
+
+ watch(filePath: string, startTime: number): Watcher;
+
+ onFileAdded(filePath: string, stat: fs.Stats): void;
+
+ onDirectoryAdded(directoryPath: string): void;
+
+ onChange(filePath: string, stat: fs.Stats): void;
+
+ onFileUnlinked(filePath: string): void;
+
+ onDirectoryUnlinked(directoryPath: string): void;
+
+ onWatcherError(): void;
+
+ doInitialScan(): void;
+
+ getTimes(): {
+ [path: string]: number;
+ };
+
+ close(): void;
+}
+export = DirectoryWatcher;
diff --git a/typings/Watcher.d.ts b/typings/Watcher.d.ts
new file mode 100644
index 0000000..efc2304
--- /dev/null
+++ b/typings/Watcher.d.ts
@@ -0,0 +1,17 @@
+///
+import { EventEmitter } from 'events'
+import DirectoryWatcher = require('./DirectoryWatcher');
+
+declare class Watcher extends EventEmitter {
+ data: number;
+ directoryWatcher: DirectoryWatcher;
+ path: string;
+ startTime: number;
+
+ constructor(directoryWatcher: DirectoryWatcher, filePath: string, startTime: number);
+
+ checkStartTime(mtime: number, initial: boolean): boolean;
+
+ close(): void;
+}
+export = Watcher;
diff --git a/typings/watchpack.d.ts b/typings/watchpack.d.ts
new file mode 100644
index 0000000..13a4533
--- /dev/null
+++ b/typings/watchpack.d.ts
@@ -0,0 +1,48 @@
+///
+import { EventEmitter } from 'events'
+import Watcher = require('./Watcher');
+
+declare class Watchpack extends EventEmitter {
+ aggregatedChanges: string[];
+ aggregateTimeout: NodeJS.Timer;
+ dirWatchers: Watcher[];
+ fileWatchers: Watcher[];
+ mtimes: {
+ [path: string]: number;
+ };
+ options: Watchpack.WatchOptions;
+ paused: boolean;
+ watcherOptions: Watchpack.WatcherOptions;
+
+ constructor(options: Watchpack.WatchOptions);
+
+ watch(files: string[], directories: string[], startTime: number): void;
+
+ close(): void;
+
+ pause(): void;
+
+ getTimes(): {
+ [path: string]: number;
+ };
+
+ _fileWatcher(file: string, watcher: Watcher): Watcher;
+
+ _dirWatcher(item: string, watcher: Watcher): Watcher;
+
+ _onChange(item: string, mtime: number, file?: string): void;
+
+ _onTimeout(): void;
+}
+
+declare namespace Watchpack {
+ interface WatcherOptions {
+ ignored?: string[] | string | RegExp | ((path: string) => boolean);
+ poll?: boolean | number;
+ }
+ interface WatchOptions extends WatcherOptions {
+ aggregateTimeout?: number;
+ }
+}
+
+export = Watchpack;