@ardatan/relay-compiler
Version:
Fork of `relay-compiler`
379 lines (306 loc) • 11.1 kB
JavaScript
/**
* Copyright (c) Facebook, Inc. and its affiliates.
*
* This source code is licensed under the MIT license found in the
* LICENSE file in the root directory of this source tree.
*
*
* @format
*/
// flowlint ambiguous-object-type:error
'use strict';
var _interopRequireDefault = require('@babel/runtime/helpers/interopRequireDefault');
var _asyncToGenerator = require('@babel/runtime/helpers/asyncToGenerator');
var _toConsumableArray2 = _interopRequireDefault(
require('@babel/runtime/helpers/toConsumableArray'),
);
var Profiler = require('../core/GraphQLCompilerProfiler');
var crypto = require('crypto');
var invariant = require('invariant');
var path = require('path'); // flowlint nonstrict-import:warn
var CodegenDirectory = /*#__PURE__*/ (function () {
function CodegenDirectory(dir, options) {
var _this = this;
var _options$filesystem, _options$shards;
this._filesystem =
(_options$filesystem = options.filesystem) !== null && _options$filesystem !== void 0
? _options$filesystem
: require('fs');
this.onlyValidate = options.onlyValidate;
this._shards =
(_options$shards = options.shards) !== null && _options$shards !== void 0
? _options$shards
: 1;
if (this._filesystem.existsSync(dir)) {
!this._filesystem.statSync(dir).isDirectory()
? process.env.NODE_ENV !== 'production'
? invariant(false, 'Expected `%s` to be a directory.', dir)
: invariant(false)
: void 0;
}
if (!this.onlyValidate) {
var dirs = [];
var parent = dir;
while (!this._filesystem.existsSync(parent)) {
dirs.unshift(parent);
parent = path.dirname(parent);
}
dirs.forEach(function (d) {
return _this._filesystem.mkdirSync(d);
});
if (this._shards > 1) {
for (var shard = 0; shard < this._shards; shard++) {
var shardDir = path.join(dir, this._getShardName(shard));
if (this._filesystem.existsSync(shardDir)) {
!this._filesystem.statSync(dir).isDirectory()
? process.env.NODE_ENV !== 'production'
? invariant(false, 'Expected `%s` to be a directory.', dir)
: invariant(false)
: void 0;
} else {
this._filesystem.mkdirSync(shardDir);
}
}
}
}
this._files = new Set();
this.changes = {
deleted: [],
updated: [],
created: [],
unchanged: [],
};
this._dir = dir;
}
CodegenDirectory.combineChanges = function combineChanges(dirs) {
var changes = {
deleted: [],
updated: [],
created: [],
unchanged: [],
};
dirs.forEach(function (dir) {
var _changes$deleted, _changes$updated, _changes$created, _changes$unchanged;
(_changes$deleted = changes.deleted).push.apply(
_changes$deleted,
(0, _toConsumableArray2['default'])(dir.changes.deleted),
);
(_changes$updated = changes.updated).push.apply(
_changes$updated,
(0, _toConsumableArray2['default'])(dir.changes.updated),
);
(_changes$created = changes.created).push.apply(
_changes$created,
(0, _toConsumableArray2['default'])(dir.changes.created),
);
(_changes$unchanged = changes.unchanged).push.apply(
_changes$unchanged,
(0, _toConsumableArray2['default'])(dir.changes.unchanged),
);
});
return changes;
};
CodegenDirectory.hasChanges = function hasChanges(changes) {
return changes.created.length > 0 || changes.updated.length > 0 || changes.deleted.length > 0;
};
CodegenDirectory.formatChanges = function formatChanges(changes, options) {
var output = [];
function formatFiles(label, files) {
if (files.length > 0) {
output.push(label + ':');
files.forEach(function (file) {
output.push(' - ' + file);
});
}
}
if (options.onlyValidate) {
formatFiles('Missing', changes.created);
formatFiles('Out of date', changes.updated);
formatFiles('Extra', changes.deleted);
} else {
formatFiles('Created', changes.created);
formatFiles('Updated', changes.updated);
formatFiles('Deleted', changes.deleted);
output.push('Unchanged: '.concat(changes.unchanged.length, ' files'));
}
return output.join('\n');
};
CodegenDirectory.printChanges = function printChanges(changes, options) {
Profiler.run('CodegenDirectory.printChanges', function () {
var output = CodegenDirectory.formatChanges(changes, options); // eslint-disable-next-line no-console
console.log(output);
});
};
CodegenDirectory.getAddedRemovedFiles = function getAddedRemovedFiles(dirs) {
var added = [];
var removed = [];
dirs.forEach(function (dir) {
dir.changes.created.forEach(function (name) {
added.push(dir.getPath(name));
});
dir.changes.deleted.forEach(function (name) {
removed.push(dir.getPath(name));
});
});
return {
added: added,
removed: removed,
};
};
CodegenDirectory.sourceControlAddRemove = /*#__PURE__*/ (function () {
var _sourceControlAddRemove = _asyncToGenerator(function* (sourceControl, dirs) {
var _CodegenDirectory$get = CodegenDirectory.getAddedRemovedFiles(dirs),
added = _CodegenDirectory$get.added,
removed = _CodegenDirectory$get.removed;
sourceControl.addRemove(added, removed);
});
function sourceControlAddRemove(_x, _x2) {
return _sourceControlAddRemove.apply(this, arguments);
}
return sourceControlAddRemove;
})();
var _proto = CodegenDirectory.prototype;
_proto.printChanges = function printChanges() {
CodegenDirectory.printChanges(this.changes, {
onlyValidate: this.onlyValidate,
});
};
_proto.read = function read(filename) {
var filePath = path.join(this._dir, filename);
if (this._filesystem.existsSync(filePath)) {
return this._filesystem.readFileSync(filePath, 'utf8');
}
return null;
};
_proto.markUnchanged = function markUnchanged(filename) {
this._addGenerated(filename);
this.changes.unchanged.push(filename);
};
/**
* Marks a files as updated or out of date without actually writing the file.
* This is probably only be useful when doing validation without intention to
* actually write to disk.
*/
_proto.markUpdated = function markUpdated(filename) {
this._addGenerated(filename);
this.changes.updated.push(filename);
};
_proto.writeFile = function writeFile(filename, content) {
var _this2 = this;
var shouldRepersist = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : false;
Profiler.run('CodegenDirectory.writeFile', function () {
_this2._addGenerated(filename);
var filePath = _this2.getPath(filename);
if (_this2._filesystem.existsSync(filePath)) {
var existingContent = _this2._filesystem.readFileSync(filePath, 'utf8');
if (existingContent === content && !shouldRepersist) {
_this2.changes.unchanged.push(filename);
} else {
_this2._writeFile(filePath, content);
_this2.changes.updated.push(filename);
}
} else {
_this2._writeFile(filePath, content);
_this2.changes.created.push(filename);
}
});
};
_proto._writeFile = function _writeFile(filePath, content) {
if (!this.onlyValidate) {
this._filesystem.writeFileSync(filePath, content, 'utf8');
}
};
/**
* Deletes all non-generated files, except for invisible "dot" files (ie.
* files with names starting with ".").
*/
_proto.deleteExtraFiles = function deleteExtraFiles(keepExtraFile) {
var _this3 = this;
Profiler.run('CodegenDirectory.deleteExtraFiles', function () {
if (_this3._shards > 1) {
_this3._filesystem.readdirSync(_this3._dir).forEach(function (firstLevel) {
if (firstLevel.startsWith('.')) {
// allow hidden files on the first level of the codegen directory
return;
}
var firstLevelPath = path.join(_this3._dir, firstLevel);
if (!_this3._filesystem.statSync(firstLevelPath).isDirectory()) {
// Delete all files on the top level, all files need to be in a
// shard directory.
_this3._filesystem.unlinkSync(firstLevelPath);
return;
}
_this3._filesystem.readdirSync(firstLevelPath).forEach(function (actualFile) {
if (keepExtraFile && keepExtraFile(actualFile)) {
return;
}
if (_this3._files.has(actualFile)) {
return;
}
if (!_this3.onlyValidate) {
try {
_this3._filesystem.unlinkSync(path.join(firstLevelPath, actualFile));
} catch (_unused) {
throw new Error(
'CodegenDirectory: Failed to delete `' +
actualFile +
'` in `' +
_this3._dir +
'`.',
);
}
}
_this3.changes.deleted.push(actualFile);
});
});
} else {
_this3._filesystem.readdirSync(_this3._dir).forEach(function (actualFile) {
if (keepExtraFile && keepExtraFile(actualFile)) {
return;
}
if (actualFile.startsWith('.') || _this3._files.has(actualFile)) {
return;
}
if (!_this3.onlyValidate) {
try {
_this3._filesystem.unlinkSync(path.join(_this3._dir, actualFile));
} catch (_unused2) {
throw new Error(
'CodegenDirectory: Failed to delete `' + actualFile + '` in `' + _this3._dir + '`.',
);
}
}
_this3.changes.deleted.push(actualFile);
});
}
});
};
_proto.getPath = function getPath(filename) {
if (this._shards > 1) {
var hasher = crypto.createHash('md5');
hasher.update(filename, 'utf8');
var shard = hasher.digest().readUInt32BE(0) % this._shards;
return path.join(this._dir, this._getShardName(shard), filename);
}
return path.join(this._dir, filename);
};
_proto._getShardName = function _getShardName(shardNumber) {
var base16length = Math.ceil(Math.log2(256) / 4);
return shardNumber.toString(16).padStart(base16length, '0');
};
_proto._addGenerated = function _addGenerated(filename) {
!!this._files.has(filename)
? process.env.NODE_ENV !== 'production'
? invariant(
false,
'CodegenDirectory: Tried to generate `%s` twice in `%s`.',
filename,
this._dir,
)
: invariant(false)
: void 0;
this._files.add(filename);
};
return CodegenDirectory;
})();
module.exports = CodegenDirectory;