mirror of
https://github.com/Foundry376/Mailspring.git
synced 2025-02-23 23:54:13 +08:00
Summary: Better-SQLite3 is a fork of node-sqlite3 which includes a re-written JavaScript interface. It’s more synchronous, but better reflects what is actually sync vs. async in sqlite’s C++ API. (Not much is really async under the hood.) This diff uses a branch of better-sqlite3 I’ve edited to support Node 6. In my tests, this branch spends 3.24x less time executing queries than `master`. (Measured time spent in calls to `this._db[run|all|get]` over the first 5000 queries of initial sync. It also increased the performance of starring a thread in the thread list by 28%. This library also allows us to use a prepared statement cache, which is great because we often make the same queries repeatedly as query subscriptions refresh the UI and deltas are dumped into the app. The old interface didn’t expose statements (cached query plans) to JS. better-sqlite3 advertises that it uses the JS garbage collector instead of lower level C++ memory management. I tested syncing my entire mailbox to verify that memory usage is not significantly different on this branch after a lot of queries have been made. Finally, it looks like we can finally stop building sqlite3 from scratch in `script/bootstrap`. This library builds properly with `apm install`. 🎉 We might want to change the DatabaseStore and DatabaseTransaction classes more, now that it’s possible to execute queries synchronously. It could make things cleaner and get us out of promise-hell in a few places. In this diff I tried to change as little as possible. Test Plan: Run tests, everything still works Reviewers: juan, jackie Reviewed By: juan, jackie Differential Revision: https://phab.nylas.com/D3315
237 lines
8.4 KiB
JavaScript
Executable file
237 lines
8.4 KiB
JavaScript
Executable file
#!/usr/bin/env node
|
|
|
|
var fs = require('fs');
|
|
var verifyRequirements = require('./utils/verify-requirements');
|
|
var safeExec = require('./utils/child-process-wrapper.js').safeExec;
|
|
var path = require('path');
|
|
|
|
var userHomeDirectory = (process.platform === 'win32') ? process.env.USERPROFILE : process.env.HOME;
|
|
var appPackageJSON = JSON.parse(fs.readFileSync(path.resolve(__dirname, '..', 'package.json')));
|
|
var targetElectronVersion = appPackageJSON['electronVersion'];
|
|
|
|
// Executes an array of commands in series
|
|
function executeCommands(commands, done, index) {
|
|
index = (index == undefined ? 0 : index);
|
|
if (index < commands.length) {
|
|
var command = commands[index];
|
|
if (command.message)
|
|
console.log(command.message);
|
|
var options = null;
|
|
if (typeof command !== 'string') {
|
|
options = command.options;
|
|
command = command.command;
|
|
}
|
|
safeExec(command, options, executeCommands.bind(this, commands, done, index + 1));
|
|
}
|
|
else
|
|
done(null);
|
|
}
|
|
|
|
function printArgs(args) {
|
|
out = "";
|
|
for(key in args) {
|
|
out += "--"+key+"="+JSON.stringify(args[key])+" ";
|
|
}
|
|
return out;
|
|
}
|
|
|
|
function bootstrap() {
|
|
var apmDestinationPath = path.resolve(__dirname, '..', 'apm');
|
|
if (!fs.existsSync(apmDestinationPath))
|
|
fs.mkdirSync(apmDestinationPath);
|
|
if (!fs.existsSync(path.join(apmDestinationPath, 'node_modules')))
|
|
fs.mkdirSync(path.join(apmDestinationPath, 'node_modules'));
|
|
|
|
var apmPath = '"' + path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager', 'bin', 'apm') + '"';
|
|
var apmFlags = process.env.JANKY_SHA1 || process.argv.indexOf('--no-color') !== -1 ? ' --no-color' : '';
|
|
|
|
var npmFlags = ' --userconfig=' + '"' + path.resolve('.npmrc') + '" ';
|
|
|
|
var gruntPath = '"' + path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'grunt') + '"';
|
|
|
|
// use the system version of npm to install build folder dependencies, including our
|
|
// own copy of NPM 3.x, which we'll use for subsequent commands
|
|
var buildInstallCommand = 'npm' + npmFlags + 'install';
|
|
var buildInstallOptions = {cwd: path.resolve(__dirname, '..', 'build')};
|
|
var npmPath = '"' + path.resolve(__dirname, '..', 'build', 'node_modules', '.bin', 'npm') + '"';
|
|
|
|
// Use our local version of npm in ./build to install apm. This ensures it gets a
|
|
// flat dependency tree.
|
|
var npmInstallApmCommand = npmPath + npmFlags + '--target=0.10.40 ' + 'install';
|
|
var npmInstallApmOptions = {cwd: apmDestinationPath};
|
|
|
|
var npmDedupeNpmCommand = npmPath + npmFlags + '--target=0.10.40 ' + 'dedupe';
|
|
var npmDedupeNpmOptions = {cwd: path.join(apmDestinationPath, 'node_modules', 'npm')};
|
|
|
|
var apmInstallCommand = apmPath + ' install' + apmFlags;
|
|
|
|
var apmEnv = JSON.parse(JSON.stringify(process.env))
|
|
apmEnv['ATOM_ELECTRON_VERSION'] = targetElectronVersion;
|
|
apmEnv['ATOM_HOME'] = path.join(userHomeDirectory, '.nylas');
|
|
|
|
var apmDedupeCommand = apmPath + ' dedupe' + apmFlags;
|
|
var semverOptions = {
|
|
cwd: path.resolve(__dirname, '..', 'apm', 'node_modules', 'atom-package-manager'),
|
|
env: apmEnv
|
|
};
|
|
|
|
var integrationCommand = npmPath + npmFlags + 'install';
|
|
var integrationOptions = {cwd: path.resolve(__dirname, '..', 'spec_integration')};
|
|
|
|
if (process.argv.indexOf('--no-quiet') === -1) {
|
|
buildInstallCommand += ' --loglevel error';
|
|
npmInstallApmCommand += ' --loglevel error';
|
|
apmInstallCommand += ' --loglevel error';
|
|
integrationCommand += ' --loglevel error';
|
|
apmDedupeCommand += ' --quiet';
|
|
|
|
npmDedupeNpmOptions.ignoreStderr = true;
|
|
npmDedupeNpmOptions.ignoreStdout = true;
|
|
buildInstallOptions.ignoreStdout = true;
|
|
npmInstallApmOptions.ignoreStdout = true;
|
|
integrationOptions.ignoreStdout = true;
|
|
}
|
|
|
|
// apm ships with 32-bit node so make sure its native modules are compiled
|
|
// for a 32-bit target architecture
|
|
if (process.env.JANKY_SHA1 && process.platform === 'win32')
|
|
npmInstallApmCommand += ' --arch=ia32';
|
|
|
|
m1 = "\n---> Installing N1 build tools\n"
|
|
m1 += " This goes inside the `build` folder and runs `npm install`\n"
|
|
m1 += " It will use the system `npm` to bootstrap our own N1 npm.\n"
|
|
m1 += " Our build tools (like Grunt) need to be compiled against Node via `npm`.\n"
|
|
m1 += " Everything else needs to be compiled against Chromium with `apm`.\n\n"
|
|
m1 += " $ "+buildInstallCommand+" "+printArgs(buildInstallOptions)+"\n"
|
|
|
|
m2 = "\n\n---> Installing apm\n"
|
|
m2 += " This installs apm via N1's `npm`\n"
|
|
m2 += " We use this local apm copy to install all N1 dependencies & packages\n\n"
|
|
m2 += " $ "+npmInstallApmCommand+" "+printArgs(npmInstallApmOptions)+"\n"
|
|
|
|
m2a = "\n\n---> Flattening apm package tree\n"
|
|
m2a += " This runs `npm dedupe` on apm's `npm` dependency.\n"
|
|
m2a += " We use this to prevent paths over 260 characters on Windows.\n\n"
|
|
m2a += " $ "+npmDedupeNpmCommand+" "+printArgs(npmDedupeNpmOptions)+"\n"
|
|
|
|
m3 = "\n\n---> Cleaning apm via `apm clean`\n"
|
|
|
|
m4 = "\n\n---> Installing N1 dependencies & packages via `apm install`\n\n"
|
|
m4 += " $ "+apmInstallCommand+"\n"
|
|
|
|
m5 = "\n\n---> De-duping packages `apm dedupe`\n\n"
|
|
m5 += " $ "+apmDedupeCommand+"\n"
|
|
|
|
m6 = "\n\n---> Request version `apm dedupe`\n\n"
|
|
m6 += " $ "+apmDedupeCommand+" request semver "+printArgs(semverOptions)+"\n"
|
|
|
|
m7 = "\n\n---> Getting latest Electron\n\n"
|
|
var gruntCmd = ""
|
|
var downloadElectronCmd = gruntPath + " download-electron --gruntfile build/Gruntfile.coffee"
|
|
m7 += " $ "+downloadElectronCmd
|
|
|
|
m8 = "\n\n---> Installing integration test modules\n\n"
|
|
m8 += " $ "+integrationCommand+" "+printArgs(integrationOptions)+"\n"
|
|
|
|
var commands = [
|
|
{
|
|
command: buildInstallCommand,
|
|
message: m1,
|
|
options: buildInstallOptions
|
|
},
|
|
{
|
|
command: gruntPath + " add-nylas-build-resources --gruntfile build/Gruntfile.coffee",
|
|
message: "",
|
|
options: {}
|
|
}
|
|
]
|
|
process.chdir(path.dirname(__dirname));
|
|
executeCommands(commands, function() {
|
|
var commands = [
|
|
{
|
|
command: npmInstallApmCommand,
|
|
message: m2,
|
|
options: npmInstallApmOptions
|
|
},
|
|
{
|
|
command: npmDedupeNpmCommand,
|
|
message: m2a,
|
|
options: npmDedupeNpmOptions
|
|
},
|
|
{
|
|
command: apmPath + ' clean' + apmFlags,
|
|
options: { env: apmEnv },
|
|
message: m3
|
|
}
|
|
];
|
|
|
|
// we need this because we don't put our modules in node_modules and npm
|
|
// install doesn't find them. Run APM install on each package directory manually.
|
|
[
|
|
path.resolve(__dirname, '..', 'internal_packages')
|
|
].forEach(function(packagesDir) {
|
|
fs.readdirSync(packagesDir).forEach(function(dir) {
|
|
var dirPackageJSONPath = path.join(packagesDir, dir, 'package.json');
|
|
// On windows and linux, invoking the apm command is very slow even when there are no
|
|
// dependencies. Make it faster by not calling unless we find there are deps.
|
|
if (fs.existsSync(dirPackageJSONPath)) {
|
|
var dirPackageJSON = JSON.parse(fs.readFileSync(dirPackageJSONPath));
|
|
if (dirPackageJSON.dependencies && (Object.keys(dirPackageJSON.dependencies).length > 0)) {
|
|
commands.push({
|
|
command: apmInstallCommand,
|
|
message: "Installing dependencies for "+dir,
|
|
options: {
|
|
cwd: path.join(packagesDir, dir),
|
|
env: apmEnv
|
|
}
|
|
});
|
|
}
|
|
}
|
|
});
|
|
});
|
|
|
|
commands = commands.concat([
|
|
{
|
|
command: apmInstallCommand,
|
|
options: { env: apmEnv },
|
|
message: m4
|
|
},
|
|
{
|
|
command: apmDedupeCommand,
|
|
options: { env: apmEnv },
|
|
message: m5
|
|
},
|
|
{
|
|
command: apmDedupeCommand + ' request semver',
|
|
options: semverOptions,
|
|
message: m6
|
|
},
|
|
{
|
|
command: downloadElectronCmd,
|
|
message: m7
|
|
},
|
|
{
|
|
command: integrationCommand,
|
|
options: integrationOptions,
|
|
message: m8
|
|
},
|
|
]);
|
|
|
|
process.chdir(path.dirname(__dirname));
|
|
executeCommands(commands, function() {
|
|
console.log("---------------------------------------------");
|
|
console.log("script/bootstrap completed successfully. You can start\nN1 via ./N1.sh --dev, or run tests with ./N1.sh --test");
|
|
process.exit();
|
|
});
|
|
});
|
|
}
|
|
|
|
verifyRequirements(function(error, successMessage) {
|
|
if (error) {
|
|
console.log(error);
|
|
process.exit(1);
|
|
}
|
|
|
|
console.log(successMessage);
|
|
bootstrap();
|
|
});
|