mirror of
https://github.com/meteor/meteor.git
synced 2026-05-02 03:01:46 -04:00
This is in addition to registering for extensions. Note that only the new SourceProcessor APIs allow this, not registerSourceHandler. The filename in question is the basename of the file. The file can be found in any directory in any package. If you want to be more picky, you can just ignore other ones in your processFilesForTarget. This introduces the SourceProcessorSet abstraction, which simplifies a lot of repeated code around matching filenames with processors and avoiding duplicates. Missing tests. See also #3985.
261 lines
10 KiB
JavaScript
261 lines
10 KiB
JavaScript
var _ = require('underscore');
|
|
var selftest = require('../selftest.js');
|
|
var files = require('../files.js');
|
|
|
|
var Sandbox = selftest.Sandbox;
|
|
|
|
var MONGO_LISTENING =
|
|
{ stdout: " [initandlisten] waiting for connections on port" };
|
|
|
|
function startRun(sandbox) {
|
|
var run = sandbox.run();
|
|
run.match("myapp");
|
|
run.match("proxy");
|
|
run.tellMongo(MONGO_LISTENING);
|
|
run.match("MongoDB");
|
|
return run;
|
|
};
|
|
|
|
// Tests the actual cache logic used by coffeescript and less.
|
|
selftest.define("compiler plugin caching - coffee/less", function () {
|
|
var s = new Sandbox({ fakeMongo: true });
|
|
|
|
// Create an app that uses coffeescript and less.
|
|
s.createApp("myapp", "coffee-and-less");
|
|
s.cd("myapp");
|
|
// Ask them to print out when they build a file (instead of using it from the
|
|
// cache) as well as when they load cache from disk.
|
|
s.set("METEOR_TEST_PRINT_CACHE_DEBUG", "t");
|
|
var run = startRun(s);
|
|
|
|
// First program built (server or web.browser) compiles everything.
|
|
run.match('Ran coffee.compile (#1) on: ' + JSON.stringify(
|
|
['/f1.coffee', '/f2.coffee', '/f3.coffee', '/packages/local-pack/p.coffee']
|
|
));
|
|
run.match('Ran less.render (#1) on: ' + JSON.stringify(
|
|
["/subdir/nested-root.main.less", "/top.main.less"]));
|
|
// Second program doesn't need to compile anything because compilation works
|
|
// the same on both programs. (Note that there is no less.render execution in
|
|
// the second program, because it has archMatching: 'web'. We'll see this
|
|
// more clearly when the next call later is "#2" --- we didn't miss a call!)
|
|
run.match("Ran coffee.compile (#2) on: []");
|
|
// App prints this:
|
|
run.match("Coffeescript X is 2 Y is 1 FromPackage is 4");
|
|
|
|
// Check that the CSS is what we expect.
|
|
var checkCSS = selftest.markStack(function (borderStyleMap) {
|
|
var builtBrowserProgramDir = files.pathJoin(
|
|
s.cwd, '.meteor', 'local', 'build', 'programs', 'web.browser');
|
|
var cssFile = _.find(
|
|
files.readdir(
|
|
files.pathJoin(s.cwd, '.meteor/local/build/programs/web.browser')),
|
|
function (path) {
|
|
return path.match(/\.css$/);
|
|
}
|
|
);
|
|
selftest.expectTrue(cssFile);
|
|
var actual = s.read(
|
|
files.pathJoin('.meteor/local/build/programs/web.browser', cssFile));
|
|
actual = actual.replace(/\s+/g, ' '); // simplify whitespace
|
|
var expected = _.map(borderStyleMap, function (style, className) {
|
|
return '.' + className + " { border-style: " + style + "; }";
|
|
}).join(' ');
|
|
selftest.expectEqual(actual, expected);
|
|
});
|
|
var expectedBorderStyles = {
|
|
el0: "dashed", el1: "dotted", el2: "solid", el3: "groove", el4: "ridge"};
|
|
checkCSS(expectedBorderStyles);
|
|
|
|
s.write("f2.coffee", "share.Y = 'Y is 3'\n");
|
|
// Only recompiles f2.
|
|
run.match('Ran coffee.compile (#3) on: ["/f2.coffee"]');
|
|
run.match('Ran less.render (#2) on: []');
|
|
// And other program doesn't even need to do f2.
|
|
run.match("Ran coffee.compile (#4) on: []");
|
|
// Program prints this:
|
|
run.match("Coffeescript X is 2 Y is 3 FromPackage is 4");
|
|
|
|
// Force a rebuild of the local package without actually changing the
|
|
// coffeescript file in it. This should not require us to coffee.compile
|
|
// anything (for either program).
|
|
s.append("packages/local-pack/package.js", "\n// foo\n");
|
|
run.match("Ran coffee.compile (#5) on: []");
|
|
run.match('Ran less.render (#3) on: []');
|
|
run.match("Ran coffee.compile (#6) on: []");
|
|
run.match("Coffeescript X is 2 Y is 3 FromPackage is 4");
|
|
|
|
// But writing to the actual source file in the local package should
|
|
// recompile.
|
|
s.write("packages/local-pack/p.coffee", "FromPackage = 'FromPackage is 5'");
|
|
run.match('Ran coffee.compile (#7) on: ["/packages/local-pack/p.coffee"]');
|
|
run.match('Ran less.render (#4) on: []');
|
|
run.match('Ran coffee.compile (#8) on: []');
|
|
run.match("Coffeescript X is 2 Y is 3 FromPackage is 5");
|
|
|
|
// Writing to a single less file only re-renders the root that depends on it.
|
|
s.write('packages/local-pack/p.less', '@el4-style: inset;\n');
|
|
expectedBorderStyles.el4 = 'inset';
|
|
run.match('Ran coffee.compile (#9) on: []');
|
|
run.match('Ran less.render (#5) on: ["/top.main.less"]');
|
|
// Note that since this was a client-only change, we're smart enough to not
|
|
// rebuild the server at all. So the next coffee.compile will be #10.
|
|
run.match("Client modified -- refreshing");
|
|
checkCSS(expectedBorderStyles);
|
|
|
|
// This works for changing a root too.
|
|
s.write('subdir/nested-root.main.less', '.el0 { border-style: double; }\n');
|
|
expectedBorderStyles.el0 = 'double';
|
|
// Only #10, not #11, because client-only changes don't rebuild the server!
|
|
run.match('Ran coffee.compile (#10) on: []');
|
|
run.match('Ran less.render (#6) on: ["/subdir/nested-root.main.less"]');
|
|
run.match("Client modified -- refreshing");
|
|
checkCSS(expectedBorderStyles);
|
|
|
|
// Adding a new root works too.
|
|
s.write('yet-another-root.main.less', '.el6 { border-style: solid; }\n');
|
|
expectedBorderStyles.el6 = 'solid';
|
|
run.match('Ran coffee.compile (#11) on: []');
|
|
run.match('Ran less.render (#7) on: ["/yet-another-root.main.less"]');
|
|
run.match("Client modified -- refreshing");
|
|
checkCSS(expectedBorderStyles);
|
|
|
|
// We never should have loaded cache from disk, since we only made
|
|
// each compiler once and there was no cache.json at this point.
|
|
run.forbid('Loaded coffeescript cache');
|
|
run.forbid('Loaded less cache');
|
|
|
|
// Kill the run. Change one coffee file and one less file and re-run.
|
|
run.stop();
|
|
s.write("f2.coffee", "share.Y = 'Y is edited'\n");
|
|
s.write('packages/local-pack/p.less', '@el4-style: double;\n');
|
|
expectedBorderStyles.el4 = 'double';
|
|
run = startRun(s);
|
|
|
|
// This time there's a cache to load!
|
|
run.match('Loaded coffeescript cache');
|
|
run.match('Loaded less cache');
|
|
// And we only need to re-compiler the changed file, even though we restarted.
|
|
run.match('Ran coffee.compile (#1) on: ["/f2.coffee"]');
|
|
run.match('Ran less.render (#1) on: ["/top.main.less"]');
|
|
run.match('Ran coffee.compile (#2) on: []');
|
|
|
|
run.match('Coffeescript X is 2 Y is edited FromPackage is 5');
|
|
checkCSS(expectedBorderStyles);
|
|
|
|
s.write('bad-import.main.less', '@import "/foo/bad.less";\n');
|
|
run.match('Errors prevented startup');
|
|
run.match('bad-import.main.less:1: Unknown import: /foo/bad.less');
|
|
run.match('Waiting for file change');
|
|
|
|
run.stop();
|
|
});
|
|
|
|
// Tests that rebuilding a compiler plugin re-instantiates the source processor,
|
|
// but other changes don't.
|
|
selftest.define("compiler plugin caching - local plugin", function () {
|
|
var s = new Sandbox({ fakeMongo: true });
|
|
|
|
s.createApp("myapp", "local-compiler-plugin");
|
|
s.cd("myapp");
|
|
|
|
var run = startRun(s);
|
|
|
|
// The compiler gets used the first time...
|
|
run.match("PrintmeCompiler invocation 1");
|
|
// ... and the program runs the generated code.
|
|
run.match("PMC: Print out bar");
|
|
run.match("PMC: Print out foo");
|
|
|
|
s.write("quux.printme", "And print out quux");
|
|
// PrintmeCompiler gets reused.
|
|
run.match("PrintmeCompiler invocation 2");
|
|
// And the right output prints out
|
|
run.match("PMC: Print out bar");
|
|
run.match("PMC: Print out foo");
|
|
run.match("PMC: And print out quux");
|
|
|
|
// Restart meteor; see that the disk cache gets used.
|
|
run.stop();
|
|
run = startRun(s);
|
|
// Disk cache gets us up to 3.
|
|
run.match("PrintmeCompiler invocation 3");
|
|
// And the right output prints out
|
|
run.match("PMC: Print out bar");
|
|
run.match("PMC: Print out foo");
|
|
run.match("PMC: And print out quux");
|
|
|
|
// Edit the compiler itself.
|
|
s.write('packages/local-plugin/plugin.js',
|
|
s.read('packages/local-plugin/plugin.js').replace(/PMC/, 'pmc'));
|
|
// New PrintmeCompiler object, and empty disk cache dir.
|
|
run.match("PrintmeCompiler invocation 1");
|
|
// And the right output prints out (lower case now)
|
|
run.match("pmc: Print out bar");
|
|
run.match("pmc: Print out foo");
|
|
run.match("pmc: And print out quux");
|
|
|
|
run.stop();
|
|
});
|
|
|
|
// Test error on duplicate compiler plugins.
|
|
selftest.define("compiler plugins - duplicate extension", () => {
|
|
const s = new Sandbox({ fakeMongo: true });
|
|
|
|
s.createApp("myapp", "duplicate-compiler-extensions");
|
|
s.cd("myapp");
|
|
|
|
let run = startRun(s);
|
|
run.match('Errors prevented startup');
|
|
run.match('conflict: two packages');
|
|
run.match('trying to handle *.myext');
|
|
|
|
// Fix it by changing one extension.
|
|
s.write('packages/local-plugin/plugin.js',
|
|
s.read('packages/local-plugin/plugin.js').replace('myext', 'xext'));
|
|
run.match('Modified -- restarting');
|
|
|
|
run.stop();
|
|
});
|
|
|
|
// Test error when a source file no longer has an active plugin.
|
|
selftest.define("compiler plugins - inactive source", () => {
|
|
const s = new Sandbox({ fakeMongo: true });
|
|
|
|
// This app depends on the published package 'glasser:uses-sourcish', and
|
|
// contains a local package 'local-plugin'.
|
|
//
|
|
// glasser:uses-sourcish depends on local-plugin and contains a file
|
|
// 'foo.sourcish'. When glasser:uses-sourcish@0.0.1 was published, a local
|
|
// copy of 'local-plugin' had a plugin which called registerCompiler for the
|
|
// extension '*.sourcish', and so 'foo.sourcish' is in the published isopack
|
|
// as a source file. However, the copy of 'local-plugin' currently in the test
|
|
// app contains no plugins. So we hit this weird error.
|
|
s.createApp('myapp', 'uses-published-package-with-inactive-source');
|
|
s.cd('myapp');
|
|
|
|
let run = startRun(s);
|
|
run.match('Errors prevented startup');
|
|
run.match('no plugin found for foo.sourcish in glasser:use-sourcish');
|
|
run.match('none is now');
|
|
|
|
run.stop();
|
|
});
|
|
|
|
// Test error when the registerCompiler callback throws.
|
|
selftest.define("compiler plugins - compiler throws", () => {
|
|
const s = new Sandbox({ fakeMongo: true });
|
|
|
|
s.createApp('myapp', 'compiler-plugin-throws-on-instantiate');
|
|
s.cd('myapp');
|
|
|
|
const run = s.run('add', 'local-plugin');
|
|
run.matchErr('Errors while adding packages');
|
|
run.matchErr('While building package local-plugin');
|
|
// XXX This is wrong! The path on disk is packages/local-plugin/plugin.js, but
|
|
// at some point we switched to the servePath which is based on the *plugin*'s
|
|
// "package" name.
|
|
run.matchErr('packages/compilePrintme/plugin.js:5:1: Error in my ' +
|
|
'registerCompiler callback!');
|
|
run.expectExit(1);
|
|
});
|