Skip to content

Commit 4c8ab80

Browse files
Merge pull request pattern-lab#577 from pattern-lab/dev
Pattern Lab Node Core 2.7.0-alpha
2 parents 821fd8e + 73c10d2 commit 4c8ab80

38 files changed

+2039
-337
lines changed

.eslintrc

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,8 @@
11
{
22
"env": {
33
"node": true,
4-
"builtin": true
4+
"builtin": true,
5+
"es6": true
56
},
67
"parserOptions": {
78
"ecmaVersion": 6,

.gitignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,3 +8,5 @@ Thumbs.db
88
source/css/style.css.map
99
.idea/
1010
public
11+
!test/patterns/public/.gitkeep
12+
!test/patterns/testDependencyGraph.json

README.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ The [command line interface](https://github.com/pattern-lab/patternlab-node/wiki
2525

2626
If you'd like to contribute to Pattern Lab Node, please do so! There is always a lot of ground to cover and something for your wheelhouse.
2727

28-
No pull request is too small. Check out any [up for grabs issues](https://github.com/pattern-lab/patternlab-node/labels/up%20for%20grabs) as a good way to get your feet wet, or add some more unit tests.
28+
No pull request is too small. Check out any [up for grabs issues](https://github.com/pattern-lab/patternlab-node/labels/help%20wanted%20%2F%20up%20for%20grabs) as a good way to get your feet wet, or add some more unit tests.
2929

3030
## Guidelines
3131
1. Please keep your pull requests concise and limited to **ONE** substantive change at a time. This makes reviewing and testing so much easier.

core/lib/changes_hunter.js

Lines changed: 81 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
"use strict";
2+
const fs = require("fs-extra"),
3+
CompileState = require('./object_factory').CompileState;
4+
5+
/**
6+
* For detecting changed patterns.
7+
* @constructor
8+
*/
9+
let ChangesHunter = function () {
10+
};
11+
12+
ChangesHunter.prototype = {
13+
14+
/**
15+
* Checks the build state of a pattern by comparing the modification date of the rendered output
16+
* file with the {@link Pattern.lastModified}. If the pattern was modified after the last
17+
* time it has been rendered, it is flagged for rebuilding via {@link CompileState.NEEDS_REBUILD}.
18+
*
19+
* @param {Pattern} pattern
20+
* @param patternlab
21+
*
22+
* @see {@link CompileState}
23+
*/
24+
checkBuildState: function (pattern, patternlab) {
25+
26+
//write the compiled template to the public patterns directory
27+
let renderedTemplatePath =
28+
patternlab.config.paths.public.patterns + pattern.getPatternLink(patternlab, 'rendered');
29+
30+
if (!pattern.compileState) {
31+
pattern.compileState = CompileState.NEEDS_REBUILD;
32+
}
33+
34+
try {
35+
// Prevent error message if file does not exist
36+
fs.accessSync(renderedTemplatePath, fs.F_OK);
37+
let outputLastModified = fs.statSync(renderedTemplatePath).mtime.getTime();
38+
39+
if (pattern.lastModified && outputLastModified > pattern.lastModified) {
40+
pattern.compileState = CompileState.CLEAN;
41+
}
42+
} catch (e) {
43+
// Output does not exist yet, needs recompile
44+
}
45+
46+
let node = patternlab.graph.node(pattern);
47+
48+
// Make the pattern known to the PatternGraph and remember its compileState
49+
if (!node) {
50+
patternlab.graph.add(pattern);
51+
} else {
52+
// Works via object reference, so we directly manipulate the node data here
53+
node.compileState = pattern.compileState;
54+
}
55+
56+
57+
},
58+
59+
/**
60+
* Updates {Pattern#lastModified} to the files modification date if the file was modified
61+
* after {Pattern#lastModified}.
62+
*
63+
* @param {Pattern} currentPattern
64+
* @param {string} file
65+
*/
66+
checkLastModified: function (currentPattern, file) {
67+
if (file) {
68+
try {
69+
let stat = fs.statSync(file);
70+
71+
// Needs recompile whenever one of the patterns files (template, json, pseudopatterns) changed
72+
currentPattern.lastModified =
73+
Math.max(stat.mtime.getTime(), currentPattern.lastModified || 0);
74+
} catch (e) {
75+
// Ignore, not a regular file
76+
}
77+
}
78+
}
79+
};
80+
81+
module.exports = ChangesHunter;

core/lib/lineage_hunter.js

Lines changed: 44 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,14 @@
11
"use strict";
22

3+
var extend = require("util")._extend;
4+
35
var lineage_hunter = function () {
46

57
var pa = require('./pattern_assembler');
68

79
function findlineage(pattern, patternlab) {
10+
// As we are adding edges from pattern to ancestor patterns, ensure it is known to the graph
11+
patternlab.graph.add(pattern);
812

913
var pattern_assembler = new pa();
1014

@@ -28,6 +32,11 @@ var lineage_hunter = function () {
2832
l.lineageState = ancestorPattern.patternState;
2933
}
3034

35+
patternlab.graph.add(ancestorPattern);
36+
37+
// Confusing: pattern includes "ancestorPattern", not the other way round
38+
patternlab.graph.link(pattern, ancestorPattern);
39+
3140
pattern.lineage.push(l);
3241

3342
//also, add the lineageR entry if it doesn't exist
@@ -44,62 +53,71 @@ var lineage_hunter = function () {
4453
}
4554

4655
ancestorPattern.lineageR.push(lr);
56+
extend(patternlab.graph.node(ancestorPattern), lr);
4757
}
4858
}
4959
});
5060
}
5161
}
5262

53-
function setPatternState(direction, pattern, targetPattern) {
54-
// if the request came from the past, apply target pattern state to current pattern lineage
63+
/**
64+
* Apply the target pattern state either to any predecessors or successors of the given
65+
* pattern in the pattern graph.
66+
* @param direction Either 'fromPast' or 'fromFuture'
67+
* @param pattern {Pattern}
68+
* @param targetPattern {Pattern}
69+
* @param graph {PatternGraph}
70+
*/
71+
function setPatternState(direction, pattern, targetPattern, graph) {
72+
var index = null;
5573
if (direction === 'fromPast') {
56-
for (var i = 0; i < pattern.lineageIndex.length; i++) {
57-
if (pattern.lineageIndex[i] === targetPattern.patternPartial) {
58-
pattern.lineage[i].lineageState = targetPattern.patternState;
59-
}
60-
}
74+
index = graph.lineage(pattern);
6175
} else {
62-
//the request came from the future, apply target pattern state to current pattern reverse lineage
63-
for (var i = 0; i < pattern.lineageRIndex.length; i++) {
64-
if (pattern.lineageRIndex[i] === targetPattern.patternPartial) {
65-
pattern.lineageR[i].lineageState = targetPattern.patternState;
66-
}
76+
index = graph.lineageR(pattern);
77+
}
78+
79+
// if the request came from the past, apply target pattern state to current pattern lineage
80+
for (var i = 0; i < index.length; i++) {
81+
if (index[i].patternPartial === targetPattern.patternPartial) {
82+
index[i].lineageState = targetPattern.patternState;
6783
}
6884
}
6985
}
7086

7187

7288
function cascadePatternStates(patternlab) {
7389

74-
var pattern_assembler = new pa();
75-
7690
for (var i = 0; i < patternlab.patterns.length; i++) {
7791
var pattern = patternlab.patterns[i];
7892

7993
//for each pattern with a defined state
8094
if (pattern.patternState) {
95+
var lineage = patternlab.graph.lineage(pattern);
8196

82-
if (pattern.lineageIndex && pattern.lineageIndex.length > 0) {
97+
if (lineage && lineage.length > 0) {
8398

8499
//find all lineage - patterns being consumed by this one
85-
for (var h = 0; h < pattern.lineageIndex.length; h++) {
86-
var lineagePattern = pattern_assembler.getPartial(pattern.lineageIndex[h], patternlab);
87-
setPatternState('fromFuture', lineagePattern, pattern);
100+
for (var h = 0; h < lineage.length; h++) {
101+
// Not needed, the graph already knows the concrete pattern
102+
// var lineagePattern = pattern_assembler.getPartial(lineageIndex[h], patternlab);
103+
setPatternState('fromFuture', lineage[h], pattern, patternlab.graph);
88104
}
89105
}
90-
91-
if (pattern.lineageRIndex && pattern.lineageRIndex.length > 0) {
106+
var lineageR = patternlab.graph.lineageR(pattern);
107+
if (lineageR && lineageR.length > 0) {
92108

93109
//find all reverse lineage - that is, patterns consuming this one
94-
for (var j = 0; j < pattern.lineageRIndex.length; j++) {
110+
for (var j = 0; j < lineageR.length; j++) {
95111

96-
var lineageRPattern = pattern_assembler.getPartial(pattern.lineageRIndex[j], patternlab);
112+
var lineageRPattern = lineageR[j];
97113

98114
//only set patternState if pattern.patternState "is less than" the lineageRPattern.patternstate
99115
//or if lineageRPattern.patternstate (the consuming pattern) does not have a state
100116
//this makes patternlab apply the lowest common ancestor denominator
101-
if (lineageRPattern.patternState === '' || (patternlab.config.patternStateCascade.indexOf(pattern.patternState)
102-
< patternlab.config.patternStateCascade.indexOf(lineageRPattern.patternState))) {
117+
let patternStateCascade = patternlab.config.patternStateCascade;
118+
let patternStateIndex = patternStateCascade.indexOf(pattern.patternState);
119+
let patternReverseStateIndex = patternStateCascade.indexOf(lineageRPattern.patternState);
120+
if (lineageRPattern.patternState === '' || (patternStateIndex < patternReverseStateIndex)) {
103121

104122
if (patternlab.config.debug) {
105123
console.log('Found a lower common denominator pattern state: ' + pattern.patternState + ' on ' + pattern.patternPartial + '. Setting reverse lineage pattern ' + lineageRPattern.patternPartial + ' from ' + (lineageRPattern.patternState === '' ? '<<blank>>' : lineageRPattern.patternState));
@@ -108,9 +126,9 @@ var lineage_hunter = function () {
108126
lineageRPattern.patternState = pattern.patternState;
109127

110128
//take this opportunity to overwrite the lineageRPattern's lineage state too
111-
setPatternState('fromPast', lineageRPattern, pattern);
129+
setPatternState('fromPast', lineageRPattern, pattern, patternlab.graph);
112130
} else {
113-
setPatternState('fromPast', pattern, lineageRPattern);
131+
setPatternState('fromPast', pattern, lineageRPattern, patternlab.graph);
114132
}
115133
}
116134
}

core/lib/object_factory.js

Lines changed: 34 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,23 @@ var Pattern = function (relPath, data, patternlab) {
7171
this.lineageR = [];
7272
this.lineageRIndex = [];
7373
this.isPseudoPattern = false;
74+
this.order = Number.MAX_SAFE_INTEGER;
7475
this.engine = patternEngines.getEngineForPattern(this);
76+
77+
/**
78+
* Determines if this pattern needs to be recompiled.
79+
*
80+
* @ee {@link CompileState}*/
81+
this.compileState = null;
82+
83+
/**
84+
* Timestamp in milliseconds when the pattern template or auxilary file (e.g. json) were modified.
85+
* If multiple files are affected, this is the timestamp of the most recent change.
86+
*
87+
* @see {@link pattern}
88+
*/
89+
this.lastModified = null;
90+
7591
};
7692

7793
// Pattern methods
@@ -140,7 +156,16 @@ Pattern.prototype = {
140156
// factory: creates an empty Pattern for miscellaneous internal use, such as
141157
// by list_item_hunter
142158
Pattern.createEmpty = function (customProps, patternlab) {
143-
var pattern = new Pattern('', null, patternlab);
159+
let relPath = '';
160+
if (customProps) {
161+
if (customProps.relPath) {
162+
relPath = customProps.relPath;
163+
} else if (customProps.subdir && customProps.filename) {
164+
relPath = customProps.subdir + path.sep + customProps.filename;
165+
}
166+
}
167+
168+
var pattern = new Pattern(relPath, null, patternlab);
144169
return extend(pattern, customProps);
145170
};
146171

@@ -152,6 +177,13 @@ Pattern.create = function (relPath, data, customProps, patternlab) {
152177
return extend(newPattern, customProps);
153178
};
154179

180+
var CompileState = {
181+
NEEDS_REBUILD: "needs rebuild",
182+
BUILDING: "building",
183+
CLEAN: "clean"
184+
};
185+
155186
module.exports = {
156-
Pattern: Pattern
187+
Pattern: Pattern,
188+
CompileState: CompileState
157189
};

0 commit comments

Comments
 (0)