mirror of
https://github.com/wekan/wekan.git
synced 2026-11-05 01:20:07 -05:00
ecmascript implies the local dynamic-import fork, so compiling ddp-server, listed before it in .meteor/versions, stopped the source-loaded server with "isopack dynamic-import not yet loaded". The loader now loads the local package asked for and retries. The source server still stops later, on "Cannot find module 'meteor/accounts-base'"; the bundle server is what the browser tests ran against. Thanks to xet7 !
264 lines
11 KiB
JavaScript
264 lines
11 KiB
JavaScript
const fs = require('fs'),
|
|
path = require('path'),
|
|
Module = require('module');
|
|
const root = process.cwd();
|
|
const tool = process.env.WEKAN_SOURCE_TOOL;
|
|
const state = path.join(root, '.tools/dev-source', process.env.PORT);
|
|
fs.mkdirSync(state, {
|
|
recursive: true
|
|
});
|
|
process.env.WEKAN_SOURCE_STATE = state;
|
|
// Cache installed/local Meteor package transforms, never application bundles.
|
|
process.env.BABEL_CACHE_DIR = path.join(state, 'package-cache');
|
|
fs.mkdirSync(process.env.BABEL_CACHE_DIR, { recursive: true });
|
|
require(path.join(tool, 'tools/tool-env/install-babel.js'));
|
|
const { convertToStandardPath: standardPath } = require(path.join(tool, 'tools/static-assets/server/mini-files'));
|
|
const {
|
|
LocalCatalog
|
|
} = require(path.join(tool, 'tools/packaging/catalog/catalog-local'));
|
|
const bp = path.join(tool, 'tools/isobuild/bundler.js');
|
|
const mod = new Module(bp, module);
|
|
mod.filename = bp;
|
|
mod.paths = Module._nodeModulePaths(path.dirname(bp));
|
|
require.cache[bp] = mod;
|
|
// Meteor does not export its in-memory targets. Adapt this one export in memory;
|
|
// the installed distribution and all application source files remain untouched.
|
|
const bundlerSource = fs.readFileSync(bp, 'utf8');
|
|
const exportMarker = 'exports.NodeModulesDirectory = NodeModulesDirectory;';
|
|
if (!bundlerSource.includes(exportMarker)) throw Error('This Meteor linker is unsupported by Dev server nobuild; update the source-loader adapter.');
|
|
mod._compile(bundlerSource.replace(exportMarker, exportMarker + ' exports.SourceClient = ClientTarget; exports.SourceServer = JsImageTarget;'), bp);
|
|
mod.loaded = true;
|
|
const toolMain = new Module(path.join(tool, 'tools/index.js'));
|
|
toolMain.filename = path.join(tool, 'tools/index.js');
|
|
toolMain.paths = Module._nodeModulePaths(path.join(tool, 'tools'));
|
|
process.mainModule = toolMain;
|
|
const bm = require(path.join(tool, 'tools/utils/buildmessage'));
|
|
const {
|
|
PackageMap
|
|
} = require(path.join(tool, 'tools/packaging/package-map'));
|
|
const {
|
|
IsopackCache
|
|
} = require(path.join(tool, 'tools/isobuild/isopack-cache'));
|
|
const {
|
|
Isopack
|
|
} = require(path.join(tool, 'tools/isobuild/isopack'));
|
|
const versions = Object.fromEntries(fs.readFileSync('.meteor/versions', 'utf8').trim().split('\n').map(l => l.split('@')));
|
|
const packagePath = (n, v) => path.resolve(process.env.METEOR_WAREHOUSE_DIR, 'packages', n.replace(/:/g, '_'), v);
|
|
require(path.join(tool, "tools/utils/fiber-helpers")).makeGlobalAsyncLocalStorage().run({}, async () => {
|
|
await require(path.join(tool, 'tools/tool-env/isopackets')).ensureIsopacketsLoadable();
|
|
const {
|
|
MongoRunner
|
|
} = require(path.join(tool, 'tools/runners/run-mongo'));
|
|
const mongoPort = Number(process.env.WEKAN_SOURCE_MONGO_PORT || Number(process.env.PORT) + 1);
|
|
if (!Number.isInteger(mongoPort) || mongoPort < 1 || mongoPort > 65535 || mongoPort === Number(process.env.PORT)) throw Error('Invalid development MongoDB port');
|
|
const mongo = new MongoRunner({
|
|
projectLocalDir: standardPath(state),
|
|
port: mongoPort,
|
|
onFailure: () => {
|
|
throw Error('Development MongoDB failed to start');
|
|
}
|
|
});
|
|
// Never terminate another development database to acquire its port.
|
|
mongo.firstStart = false;
|
|
let closing = false;
|
|
const stop = async code => {
|
|
if (closing) return;
|
|
closing = true;
|
|
mongo.stop();
|
|
if (global.Package?.webapp?.WebApp?.httpServer) Package.webapp.WebApp.httpServer.close();
|
|
const net = require('node:net');
|
|
const deadline = Date.now() + 15000;
|
|
while (Date.now() < deadline) {
|
|
const open = await new Promise(resolve => {
|
|
const socket = net.connect({
|
|
host: '127.0.0.1',
|
|
port: mongo.port
|
|
});
|
|
socket.on('connect', () => {
|
|
socket.destroy();
|
|
resolve(true);
|
|
});
|
|
socket.on('error', () => resolve(false));
|
|
socket.setTimeout(500, () => {
|
|
socket.destroy();
|
|
resolve(false);
|
|
});
|
|
});
|
|
if (!open) break;
|
|
await new Promise(resolve => setTimeout(resolve, 100));
|
|
}
|
|
process.exit(code);
|
|
};
|
|
process.on('SIGINT', () => stop(130));
|
|
process.on('SIGTERM', () => stop(143));
|
|
process.on('exit', () => mongo.stop());
|
|
await mongo.start();
|
|
if (!mongo.handle) throw Error('Development MongoDB did not become ready');
|
|
if (process.send) process.send({ type: 'source-mongo-started', port: mongoPort });
|
|
process.env.MONGO_URL = mongo.mongoUrl().replace(/meteor$/, process.env.WEKAN_SOURCE_MONGO_DATABASE === 'wekan' ? 'wekan' : 'meteor');
|
|
process.env.MONGO_OPLOG_URL = mongo.oplogUrl();
|
|
console.log(`Source server: ${process.env.ROOT_URL}; bundled development MongoDB: ${process.env.MONGO_URL}`);
|
|
const messages = await require(path.join(tool, "tools/utils/fiber-helpers")).makeGlobalAsyncLocalStorage().run({}, () => bm.capture(async () => {
|
|
await require(path.join(tool, 'tools/packaging/catalog/catalog')).official.initialize({
|
|
offline: true
|
|
});
|
|
const localCatalog = new LocalCatalog();
|
|
await localCatalog.initialize({
|
|
localPackageSearchDirs: [standardPath(path.join(root, 'packages'))]
|
|
});
|
|
const packageMap = new PackageMap(versions, {
|
|
localCatalog
|
|
});
|
|
const cache = new IsopackCache({
|
|
packageMap,
|
|
tropohouse: {
|
|
packagePath: (name, version) => standardPath(packagePath(name, version))
|
|
}
|
|
});
|
|
for (const [name, version] of Object.entries(versions)) {
|
|
let dir = packagePath(name, version);
|
|
if (localCatalog.getPackageSource(name)) continue;
|
|
if (!fs.existsSync(dir)) throw Error(`missing ${name}`);
|
|
const iso = new Isopack();
|
|
await iso.initFromPath(name, standardPath(dir));
|
|
cache._isopacks[name] = iso;
|
|
}
|
|
// A prebuilt package can imply one of WeKan's local package forks -
|
|
// ecmascript implies the local dynamic-import - so compiling an earlier
|
|
// local package (ddp-server) can ask for a local one .meteor/versions lists
|
|
// after it: "isopack dynamic-import not yet loaded". Load that one first,
|
|
// then retry.
|
|
const loadLocal = async (name, asked = new Set()) => {
|
|
for (;;) {
|
|
try {
|
|
return await cache._ensurePackageLoaded(name, { [name]: true });
|
|
} catch (error) {
|
|
const missing = /isopack (\S+) not yet loaded/.exec(error && error.message);
|
|
if (!missing || asked.has(missing[1]) || !localCatalog.getPackageSource(missing[1])) throw error;
|
|
asked.add(missing[1]);
|
|
await loadLocal(missing[1], asked);
|
|
}
|
|
}
|
|
};
|
|
for (const name of Object.keys(versions)) if (localCatalog.getPackageSource(name)) await loadLocal(name);
|
|
const use = fs.readFileSync('.meteor/packages', 'utf8').split('\n').map(x => x.split('#')[0].trim().split('@')[0]).filter(x => x && !['rspack', 'standard-minifier-css', 'standard-minifier-js', 'meteortesting:mocha', 'hot-module-replacement'].includes(x));
|
|
const t = new mod.exports.SourceServer({
|
|
packageMap,
|
|
isopackCache: cache,
|
|
sourceRoot: standardPath(root),
|
|
arch: require(path.join(tool, 'tools/utils/archinfo')).host(),
|
|
buildMode: 'development'
|
|
});
|
|
console.log('Linking packages', use.length);
|
|
await t.make({
|
|
packages: use,
|
|
minifyMode: 'development'
|
|
});
|
|
const browser = require('./browser.cjs');
|
|
const client = new mod.exports.SourceClient({
|
|
packageMap,
|
|
isopackCache: cache,
|
|
sourceRoot: standardPath(root),
|
|
arch: 'web.browser',
|
|
buildMode: 'development'
|
|
});
|
|
await client.make({
|
|
packages: use,
|
|
minifyMode: 'development',
|
|
minifiers: []
|
|
});
|
|
await browser.prepare(client);
|
|
const image = t.toJsImage();
|
|
console.log('Linked', image.jsToLoad.length);
|
|
global.__meteor_bootstrap__ = {
|
|
startupHooks: [],
|
|
serverDir: path.join(state, 'server'),
|
|
configJson: {
|
|
clientPaths: {
|
|
'web.browser': '../web.browser'
|
|
}
|
|
},
|
|
isFibersDisabled: true
|
|
};
|
|
global.__meteor_runtime_config__ = {
|
|
meteorRelease: fs.readFileSync('.meteor/release', 'utf8').trim(),
|
|
ROOT_URL: process.env.ROOT_URL,
|
|
ROOT_URL_PATH_PREFIX: new URL(process.env.ROOT_URL).pathname.replace(/\/+$/, '')
|
|
};
|
|
global.Package = await image.load({
|
|
__meteor_bootstrap__,
|
|
__meteor_runtime_config__,
|
|
dynamicImportInfo: {
|
|
server: {
|
|
dynamicRoot: path.join(state, 'dynamic')
|
|
}
|
|
}
|
|
});
|
|
for (const [name, exports] of Object.entries(Package)) {
|
|
if (name.startsWith('_')) continue;
|
|
for (const [key, value] of Object.entries(exports)) if (key !== 'global' && key !== 'main' && !(key in global)) global[key] = value;
|
|
const id = path.join(state, 'meteor', name + '.js');
|
|
require.cache[id] = {
|
|
id,
|
|
filename: id,
|
|
loaded: true,
|
|
exports
|
|
};
|
|
}
|
|
const resolve = Module._resolveFilename;
|
|
Module._resolveFilename = function (id, parent, ...args) {
|
|
if (id.startsWith('meteor/') && Package[id.slice(7)]) return path.join(state, 'meteor', id.slice(7) + '.js');
|
|
if (id.startsWith('/') && !id.startsWith(root + '/') && (fs.existsSync(root + id) || fs.existsSync(root + id + '.js'))) id = root + id;
|
|
return resolve.call(this, id, parent, ...args);
|
|
};
|
|
const {
|
|
transform
|
|
} = require('./transform.cjs');
|
|
const js = Module._extensions['.js'];
|
|
Module._extensions['.js'] = function (m, file) {
|
|
const rel = path.relative(root, file);
|
|
if (/^(imports|models|server|client|config)[/\\]/.test(rel) || rel === 'sandstorm.js') {
|
|
const source = fs.readFileSync(file, 'utf8');
|
|
return m._compile(transform(source, file, 'inline'), file);
|
|
}
|
|
return js(m, file);
|
|
};
|
|
const privatePath = file => {
|
|
const base = path.join(root, 'private');
|
|
const result = path.resolve(base, file);
|
|
if (!result.startsWith(base + path.sep)) throw Error('Invalid private asset path');
|
|
return result;
|
|
};
|
|
global.Assets = {
|
|
getTextAsync: async file => fs.promises.readFile(privatePath(file), 'utf8'),
|
|
getBinaryAsync: async file => fs.promises.readFile(privatePath(file)),
|
|
absoluteFilePath: file => privatePath(file)
|
|
};
|
|
global.Npm = {
|
|
require: Module.createRequire(path.join(root, 'package.json'))
|
|
};
|
|
console.log('Loading source server');
|
|
require(path.join(root, 'server/main.js'));
|
|
Package.webapp.WebApp.rawConnectHandlers.use(browser.middleware);
|
|
Package.webapp.WebApp.rawConnectHandlers.use(__meteor_runtime_config__.ROOT_URL_PATH_PREFIX || '/', Package.webapp.WebApp.express.static(path.join(root, 'public')));
|
|
console.log('Running startup hooks');
|
|
while (__meteor_bootstrap__.startupHooks.length) await __meteor_bootstrap__.startupHooks.shift()();
|
|
__meteor_bootstrap__.startupHooks = null;
|
|
await Package.webapp.main([]);
|
|
console.log('Ready');
|
|
if (process.env.WEKAN_SOURCE_VISUALIZE === '1') console.log('Source module visualizer: ' + process.env.ROOT_URL.replace(/\/+$/, '') + '/__source/visualizer');
|
|
if (process.env.WEKAN_SOURCE_TESTS === '1') stop(await require('./server-tests.cjs')({
|
|
root,
|
|
packagePath,
|
|
versions
|
|
}));
|
|
}));
|
|
if (messages.hasMessages()) {
|
|
console.error(messages.formatMessages());
|
|
stop(1);
|
|
}
|
|
}).catch(e => {
|
|
console.error(e);
|
|
process.exit(1);
|
|
});
|