Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Derby 2 #612

Merged
merged 48 commits into from
Dec 12, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
e35061d
Add eslint; remove jshint
craigbeck Oct 3, 2022
4333095
Automated eslint --fix
craigbeck Oct 3, 2022
828b486
Use conditional calls
craigbeck Oct 4, 2022
068fe02
Use default parameters
craigbeck Oct 4, 2022
5ef8a78
Allow double-eq comparison with `null`
craigbeck Oct 4, 2022
3714c7b
Conditional assignments
craigbeck Oct 4, 2022
aec9f7b
Include tests in lint; add script for checks
craigbeck Oct 4, 2022
779642d
Update github workflow to use checks script
craigbeck Oct 4, 2022
afd2e90
Merge branch 'master' of github.com:derbyjs/derby into use-eslint
craigbeck Oct 4, 2022
366cbca
Update test matrix
craigbeck Oct 4, 2022
d36c132
Merge branch 'derby-2' of github.com:derbyjs/derby into use-eslint
craigbeck Oct 4, 2022
3cbe332
Update dependencies
craigbeck Oct 5, 2022
1266cda
Use conditional to ensure previous has value
craigbeck Oct 6, 2022
3bc388d
Add semicolons
craigbeck Oct 6, 2022
d2cfca5
Merge pull request #604 from derbyjs/use-eslint
craigbeck Oct 6, 2022
76f81cd
Merge branch 'derby-2' of github.com:derbyjs/derby into update-deps
craigbeck Oct 6, 2022
e5bf27d
Drop node 12 from test matrix as JSDom uses optional chaining operator
craigbeck Oct 6, 2022
4b75612
Extract browserify related code to bundler/
craigbeck Oct 13, 2022
d7d6e61
Remove console.logs
craigbeck Oct 19, 2022
9d607c3
Fix lazy creation of bindings + tests
craigbeck Oct 27, 2022
2486747
Fix test on child binding
craigbeck Oct 27, 2022
ba742f6
Merge pull request #608 from derbyjs/derby-2-fix-binding
craigbeck Oct 27, 2022
2f80d77
Add behavioral test for binding bug with view function calls on array…
ericyhwang Oct 27, 2022
4f71d5c
Merge pull request #606 from derbyjs/update-deps
craigbeck Oct 31, 2022
979e04f
Merge branch 'derby-2' of github.com:derbyjs/derby into derby-2-brows…
craigbeck Oct 31, 2022
cdefe3c
Merge pull request #609 from derbyjs/test-array-item-function
ericyhwang Nov 2, 2022
cfb2cee
Setup Derby for derby-webpack plugin
craigbeck Nov 2, 2022
4c2e49a
Merge branch 'derby-2' of github.com:derbyjs/derby into derby-2-webpack
craigbeck Nov 2, 2022
dc65dfd
Merge branch 'master' into derby-2-webpack
craigbeck Nov 2, 2022
fbd0079
Remove node 12 from test matrix
craigbeck Nov 2, 2022
6312f2d
Use var
craigbeck Nov 2, 2022
5f38c49
Revert to use var over const/let
craigbeck Nov 2, 2022
7b97794
Throw error if bundler used in browser bundle
craigbeck Nov 2, 2022
51f46ae
Merge branch 'derby-2-browserify' of github.com:derbyjs/derby into de…
craigbeck Nov 3, 2022
908ef15
Merge pull request #607 from derbyjs/derby-2-browserify
craigbeck Nov 3, 2022
4578b53
Merge branch 'derby-2' of github.com:derbyjs/derby into derby-2-webpack
craigbeck Nov 3, 2022
3810c69
Merge branch 'master' of github.com:derbyjs/derby into derby-2
craigbeck Nov 15, 2022
7df4404
Merge branch 'master' of github.com:derbyjs/derby into derby-2
craigbeck Nov 15, 2022
6c67a7e
Update version for rc
craigbeck Nov 28, 2022
3689e5c
Merge branch 'derby-2' of github.com:derbyjs/derby into derby-2-webpack
craigbeck Nov 28, 2022
227045d
Fix method call
craigbeck Dec 6, 2022
eb8bf35
v2.0.0-rc.2
craigbeck Dec 6, 2022
0f22d90
Fix selecting initial data JSON
craigbeck Dec 6, 2022
6526601
v2.0.0-rc.3
craigbeck Dec 6, 2022
8fe229d
Add .vscode to gitignore
craigbeck Dec 9, 2022
ceca89d
Merge pull request #610 from derbyjs/derby-2-webpack
craigbeck Dec 12, 2022
b0b6923
Remove internal bundler
craigbeck Dec 12, 2022
010bc28
Revert to ES5 syntax
craigbeck Dec 12, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
44 changes: 44 additions & 0 deletions .eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
{
"env": {
"node": true
},
"parserOptions": {
"ecmaVersion": 8
},
"globals": {
"window": false,
"document": false
},
"rules": {
"comma-style": [
2,
"last"
],
"eqeqeq": ["error", "always", {"null": "ignore"}],
"indent": [
2,
2,
{
"SwitchCase": 1
}
],
"new-cap": 2,
"quotes": [
2,
"single"
],
"no-undef": 2,
"no-shadow": 0,
"no-unused-expressions": 2,
"no-cond-assign": [
2,
"except-parens"
]
},
"overrides": [
{
"files": ["test/**/*.mocha.js", "test/browser/*.js"],
"env": {"mocha": true, "node": true}
}
]
}
5 changes: 3 additions & 2 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,9 @@ jobs:
strategy:
matrix:
node:
- 12
- 14
- 16
- 18
timeout-minutes: 10
steps:
- uses: actions/checkout@v2
Expand All @@ -28,4 +29,4 @@ jobs:
- name: Install
run: npm install
- name: Test
run: npm run test
run: npm run checks
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.DS_Store
*.swp
node_modules
.vscode
18 changes: 0 additions & 18 deletions .jshintrc

This file was deleted.

44 changes: 39 additions & 5 deletions lib/App.js
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,17 @@ var serializedViews = require('./_views');

module.exports = App;

globalThis.APPS = globalThis.APPS || new Map();

App.register = function register(name) {
if (!globalThis.APPS.has(name)) {
globalThis.APPS.set(name, {
attached: false,
initialState: null,
});
}
}

function App(derby, name, filename, options) {
EventEmitter.call(this);
this.derby = derby;
Expand All @@ -33,6 +44,7 @@ function App(derby, name, filename, options) {
this.page = null;
this._pendingComponentMap = {};
this._init(options);
App.register(name);
}

function createAppPage(derby) {
Expand All @@ -58,9 +70,10 @@ App.prototype._init = function() {
// Must also wait for content ready so that bundle is fully downloaded.
this._contentReady();
};

App.prototype._finishInit = function() {
var script = this._getScript();
var data = App._parseInitialData(script.nextSibling.innerHTML);
var script = this._getAppStateScript();
var data = App._parseInitialData(script.textContent);
this.model.createConnection(data);
this.emit('model', this.model);
util.isProduction = data.nodeEnv === 'production';
Expand All @@ -72,7 +85,7 @@ App.prototype._finishInit = function() {
this._waitForAttach = false;
// Instead of attaching, do a route and render if a link was clicked before
// the page finished attaching
if (this._cancelAttach) {
if (this._cancelAttach || this._isAttached()) {
this.history.refresh();
return;
}
Expand All @@ -91,6 +104,27 @@ App.prototype._finishInit = function() {
}
this.emit('load', page);
};

App.prototype._isAttached = function isInitialized() {
var attached = globalThis.APPS.get(this.name).attached;
return attached;
}

App.prototype._persistInitialState = function persistInitialState(state) {
if (this._isAttached()) {
return;
}
globalThis.APPS.set(this.name, {
attached: true,
initialState: state,
});
}

App.prototype._initialState = function initialState() {
var initialState = globalThis.APPS.get(this.name).initialState;
return initialState;
}

// Modified from: https://github.com/addyosmani/jquery.parts/blob/master/jquery.documentReady.js
App.prototype._contentReady = function() {
// Is the DOM ready to be used? Set to true once it occurs.
Expand Down Expand Up @@ -166,8 +200,8 @@ App.prototype._contentReady = function() {
}
};

App.prototype._getScript = function() {
return document.querySelector('script[data-derby-app]');
App.prototype._getAppStateScript = function() {
return document.querySelector('script[data-derby-app-state]');
};

App.prototype.use = util.use;
Expand Down
111 changes: 15 additions & 96 deletions lib/AppForServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -26,8 +26,8 @@ if (module.require) {
var STYLE_EXTENSIONS = ['.css'];
var VIEW_EXTENSIONS = ['.html'];
var COMPILERS = {
'.css': cssCompiler
, '.html': htmlCompiler
'.css': cssCompiler,
'.html': htmlCompiler
};
function cssCompiler(file, filename, options) {
return {css: file, files: [filename]};
Expand Down Expand Up @@ -116,102 +116,21 @@ AppForServer.prototype.createPage = function(req, res, next) {
};

AppForServer.prototype.bundle = function(backend, options, cb) {
var app = this;
if (typeof options === 'function') {
cb = options;
options = null;
}
options || (options = {});
if (options.minify == null) options.minify = util.isProduction;
// Turn all of the app's currently registered views into a javascript
// function that can recreate them in the client
var viewsSource = this._viewsSource(options);
var bundleFiles = [];
backend.once('bundle', function(bundle) {
bundle.require(path.dirname(__dirname), {expose: 'derby'});
// Hack to inject the views script into the Browserify bundle by replacing
// the empty _views.js file with the generated source
var viewsFilename = require.resolve('./_views');
bundle.transform(function(filename) {
if (filename !== viewsFilename) return through();
return through(
function write() {}
, function end() {
this.queue(viewsSource);
this.queue(null);
}
);
}, {global: true});
bundle.on('file', function(filename) {
bundleFiles.push(filename);
});
app.emit('bundle', bundle);
});
backend.bundle(app.filename, options, function(err, source, map) {
if (err) return cb(err);
app.scriptHash = crypto.createHash('md5').update(source).digest('hex');
source = source.replace('{{DERBY_SCRIPT_HASH}}', app.scriptHash);
source = source.replace(/['"]{{DERBY_BUNDLED_AT}}['"]/, Date.now());
if (app.watchFiles) {
app._autoRefresh(backend);
app._watchBundle(bundleFiles);
}
cb(null, source, map);
});
throw new Error(
'bundle implementation missing; use racer-bundler for implementation, or remove call to this method and use another bundler',
);
};

AppForServer.prototype.writeScripts = function(backend, dir, options, cb) {
var app = this;
this.bundle(backend, options, function(err, source, map) {
if (err) return cb(err);
dir = path.join(dir, 'derby');
if (!fs.existsSync(dir)) fs.mkdirSync(dir);
var filename = app.name + '-' + app.scriptHash;
var base = path.join(dir, filename);
app.scriptUrl = app.scriptBaseUrl + '/derby/' + filename + '.js';

// Write current map and bundle files
if (!(options && options.disableScriptMap)) {
app.scriptMapUrl = app.scriptMapBaseUrl + '/derby/' + filename + '.map.json';
source += '\n//# sourceMappingURL=' + app.scriptMapUrl;
app.scriptMapFilename = base + '.map.json';
fs.writeFileSync(app.scriptMapFilename, map, 'utf8');
}
app.scriptFilename = base + '.js';
fs.writeFileSync(app.scriptFilename, source, 'utf8');

// Delete app bundles with same name in development so files don't
// accumulate. Don't do this automatically in production, since there could
// be race conditions with multiple processes intentionally running
// different versions of the app in parallel out of the same directory,
// such as during a rolling restart.
if (app.watchFiles) {
var appPrefix = app.name + '-';
var currentBundlePrefix = appPrefix + app.scriptHash;
var filenames = fs.readdirSync(dir);
for (var i = 0; i < filenames.length; i++) {
var filename = filenames[i];
if (filename.indexOf(appPrefix) !== 0) {
// Not a bundle for this app, skip.
continue;
}
if (filename.indexOf(currentBundlePrefix) === 0) {
// Current (newly written) bundle for this app, skip.
continue;
}
// Older bundle for this app, clean it up.
var oldFilename = path.join(dir, filename);
fs.unlinkSync(oldFilename);
}
}
cb && cb();
});
throw new Error(
'writeScripts implementation missing; use racer-bundler for implementation, or remove call to this method and use another bundler',
);
};

AppForServer.prototype._viewsSource = function(options) {
return '/*DERBY_SERIALIZED_VIEWS*/' +
'module.exports = ' + this.views.serialize(options) + ';' +
'/*DERBY_SERIALIZED_VIEWS_END*/';
return `/*DERBY_SERIALIZED_VIEWS ${this.name}*/\n` +
'module.exports = ' + this.views.serialize(options) + ';\n' +
`/*DERBY_SERIALIZED_VIEWS_END ${this.name}*/\n`;
};

AppForServer.prototype.serialize = function() {
Expand All @@ -229,10 +148,10 @@ AppForServer.prototype.serialize = function() {
this.scriptMapUrl.slice(this.scriptMapBaseUrl.length) :
this.scriptMapUrl;
var serialized = JSON.stringify({
scriptBaseUrl: this.scriptBaseUrl
, scriptMapBaseUrl: this.scriptMapBaseUrl
, scriptUrl: scriptUrl
, scriptMapUrl: scriptMapUrl
scriptBaseUrl: this.scriptBaseUrl,
scriptMapBaseUrl: this.scriptMapBaseUrl,
scriptUrl: scriptUrl,
scriptMapUrl: scriptMapUrl
});
fs.writeFileSync(this.serializedBase + '.json', serialized, 'utf8');
};
Expand Down
2 changes: 1 addition & 1 deletion lib/Dom.js
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ Dom.prototype.addListener = function(type, target, listener, useCapture) {
}
var domListener =
(type === 'destroy') ? new DestroyListener(target, listener) :
new DomListener(type, target, listener, useCapture);
new DomListener(type, target, listener, useCapture);
if (-1 === this._listenerIndex(domListener)) {
var listeners = this._listeners || this._initListeners();
listeners.push(domListener);
Expand Down
4 changes: 3 additions & 1 deletion lib/Page.js
Original file line number Diff line number Diff line change
Expand Up @@ -126,7 +126,9 @@ Page.prototype.destroy = function() {
silentModel.destroy('_page');
silentModel.destroy('$components');
// Unfetch and unsubscribe from all queries and documents
silentModel.unloadAll && silentModel.unloadAll();
if (silentModel.unloadAll) {
silentModel.unloadAll();
}
};

Page.prototype._addModelListeners = function(eventModel) {
Expand Down
24 changes: 5 additions & 19 deletions lib/PageForServer.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,23 +29,9 @@ PageForServer.prototype.render = function(status, ns) {
this._setRenderParams(ns);
var pageHtml = this.get('Page', ns);
this.res.write(pageHtml);
this.app.emit('htmlDone', this);

var bundleScriptTag = '<script async data-derby-app src="' + this.app.scriptUrl + '"';
if (this.app.scriptCrossOrigin) {
// Scripts loaded from a different origin (such as a CDN) won't report
// much information to the host page's window.onerror. Adding the
// "crossorigin" attribute to the script tag allows reporting of detailed
// error info to the host page.
// HOWEVER - if the "crossorigin" attribute is present for a script tag
// with a cross-origin "src", then the script's HTTP response MUST have
// an appropriate "Access-Control-Allow-Origin" header set. Otherwise,
// the browser will refuse to load the script.
bundleScriptTag += ' crossorigin';
}
bundleScriptTag += '></script>';
this.res.write(bundleScriptTag);

this.res.write('<script type="application/json">');
this.res.write('<script data-derby-app-state type="application/json">');
var tailHtml = this.get('Tail', ns);

this.model.destroy('$components');
Expand Down Expand Up @@ -96,9 +82,9 @@ function stringifyBundle(bundle) {
// TODO: Cleanup; copied from tracks
function pageParams(req) {
var params = {
url: req.url
, body: req.body
, query: req.query
url: req.url,
body: req.body,
query: req.query,
};
for (var key in req.params) {
params[key] = req.params[key];
Expand Down
9 changes: 7 additions & 2 deletions lib/eventmodel.js
Original file line number Diff line number Diff line change
Expand Up @@ -256,8 +256,13 @@ EventModel.prototype._removeItemContext = function(context) {
};

EventModel.prototype._addBinding = function(binding) {
var bindings = this.bindings || (this.bindings = new BindingsMap());
binding.eventModels || (binding.eventModels = new EventModelsMap());
if (this.bindings == null) {
this.bindings = new BindingsMap();
}
var bindings = this.bindings;
if (binding.eventModels == null) {
binding.eventModels = new EventModelsMap();
}
bindings[binding.id] = binding;
binding.eventModels[this.id] = this;
};
Expand Down
4 changes: 3 additions & 1 deletion lib/files.js
Original file line number Diff line number Diff line change
Expand Up @@ -54,7 +54,9 @@ function loadViewsSync(app, sourceFilename, namespace) {
}

function loadStylesSync(app, sourceFilename, options) {
options || (options = {compress: util.isProduction});
if (options == null) {
options = { compress: util.isProduction };
}
var resolved = resolve.sync(sourceFilename, {
extensions: app.styleExtensions,
packageFilter: deleteMain}
Expand Down
Loading