Skip to content
This repository has been archived by the owner on Mar 2, 2019. It is now read-only.

Commit

Permalink
Added a basic build pipeline.
Browse files Browse the repository at this point in the history
  • Loading branch information
jameswilddev committed Sep 25, 2018
1 parent 93fc0c7 commit 5a814c2
Show file tree
Hide file tree
Showing 23 changed files with 3,626 additions and 7 deletions.
5 changes: 5 additions & 0 deletions .babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
{
"presets": [
"@babel/env"
]
}
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1 +1,2 @@
node_modules
dist
9 changes: 2 additions & 7 deletions .travis.yml
Original file line number Diff line number Diff line change
@@ -1,10 +1,5 @@
language: node_js
node_js:
- node
jobs:
include:
- stage: dummy
install:
- echo TODO install
script:
- echo TODO test
script:
- npm run-script oneOff
19 changes: 19 additions & 0 deletions .vscode/tasks.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Watch for changes",
"type": "shell",
"command": "npm run-script watch",
"group": "build",
"problemMatcher": []
},
{
"label": "One-off build",
"type": "shell",
"command": "npm run-script oneOff",
"group": "build",
"problemMatcher": []
}
]
}
17 changes: 17 additions & 0 deletions build/createDirectoryStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as path from "path"
import mkdirp from "mkdirp"
import Stage from "./stage"

export default class CreateDirectoryStage extends Stage {
constructor(parent, name, dependencies, pathSegmentFactory) {
super(parent, name, dependencies)
this.pathSegmentFactory = pathSegmentFactory
}

performStart() {
mkdirp(
path.join.apply(path, this.pathSegmentFactory()),
error => this.handle(error, () => this.done())
)
}
}
17 changes: 17 additions & 0 deletions build/deleteDirectoryStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import * as path from "path"
import rimraf from "rimraf"
import Stage from "./stage"

export default class DeleteDirectoryStage extends Stage {
constructor(parent, name, dependencies, pathSegmentFactory) {
super(parent, name, dependencies)
this.pathSegmentFactory = pathSegmentFactory
}

performStart() {
rimraf(
path.join.apply(path, this.pathSegmentFactory()),
error => this.handle(error, () => this.done())
)
}
}
14 changes: 14 additions & 0 deletions build/fileListStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import * as fs from "fs"
import * as path from "path"
import InstancedStage from "./instancedStage"

export default class FileListStage extends InstancedStage {
constructor(parent, name, dependencies, instanceFactory, searchPathFactory) {
super(parent, name, dependencies, instanceFactory)
this.searchPathFactory = searchPathFactory
}

getInstanceNames() {
fs.readdir(path.join.apply(path, this.searchPathFactory()), (error, files) => this.handle(error, () => this.gotInstanceNames(files)))
}
}
15 changes: 15 additions & 0 deletions build/fileSearchStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
import * as path from "path"
import recursiveReaddir from "recursive-readdir"
import InstancedStage from "./instancedStage"

export default class FileSearchStage extends InstancedStage {
constructor(parent, name, dependencies, instanceFactory, searchPathFactory, extension) {
super(parent, name, dependencies, instanceFactory)
this.searchPathFactory = searchPathFactory
this.extension = extension
}

getInstanceNames() {
recursiveReaddir(path.join.apply(path, this.searchPathFactory()), (error, files) => this.handle(error, () => this.gotInstanceNames(files.filter(file => file.endsWith(`.${this.extension}`)))))
}
}
30 changes: 30 additions & 0 deletions build/gameStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import WatchableStage from "./watchableStage"
import JavaScriptParseStage from "./javaScriptParseStage"
import JavaScriptCombineStage from "./javaScriptCombineStage"
import WriteFileStage from "./writeFileStage"
import DeleteDirectoryStage from "./deleteDirectoryStage"
import CreateDirectoryStage from "./createDirectoryStage"

export default class GameStage extends WatchableStage {
constructor(parent, name, dependencies, engine) {
super(parent, name, dependencies)
const parseJavaScript = new JavaScriptParseStage(this, `parseJavaScript`, [], () => [`src`, `games`, name])
this.watchInstanced(() => [`src`, `games`, name], parseJavaScript, `read`, null)
const combineJavaScript = new JavaScriptCombineStage(
this,
`combineJavaScript`,
[engine, parseJavaScript],
() => Object
.keys(engine.parsed)
.map(key => engine.parsed[key])
.concat(
Object
.keys(parseJavaScript.parsed)
.map(key => parseJavaScript.parsed[key])
)
)
const deleteDistDirectory = new DeleteDirectoryStage(this, `deleteDistDirectory`, [combineJavaScript], () => [`dist`, name])
const createDistDirectory = new CreateDirectoryStage(this, `createDistDirectory`, [deleteDistDirectory], () => [`dist`, name])
new WriteFileStage(this, `writeJavaScript`, [createDistDirectory], () => [`dist`, name, `index.js`], () => combineJavaScript.code)
}
}
75 changes: 75 additions & 0 deletions build/groupStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,75 @@
import Stage from "./stage"
import * as stage from "./stage"

export default class GroupStage extends Stage {
constructor(parent, name, dependencies) {
super(parent, name, dependencies)
this.children = []
}

blocksChildren() {
switch (this.state) {
case `running`:
case `restarting`:
if (this.parent) {
return this.parent.blocksChildren()
} else {
return false
}
case `blocked`:
case `done`:
case `failed`:
return true
default:
this.criticalStop(`State "${this.state}" is not implemented by "blocksChildren".`)
}
}

checkState() {
switch (this.state) {
case `running`:
case `restarting`:
if (!this.children.every(child => {
switch (child.state) {
case `restarting`:
case `running`:
return false

case `blocked`:
return !child.couldStart()

case `done`:
case `failed`:
return true

default:
this.criticalStop(`State "${child.state}" is not implemented by "checkState" when inspecting child "${child.fullName}".`)
}
})) {
return
}

this.handle(this.children.some(child => child.state != `done`) && `One or more children failed to process.`, () => this.done())
break

case `blocked`:
case `done`:
case `failed`:
super.checkState()
break
default:
this.criticalStop(`State "${this.state}" is not implemented by "checkState" (derivced).`)
}
}

performStart() {
stage.handleChanges()
}

stop() {
super.stop()
while (this.children.length) {
this.children[0].stop()
}
}
}
80 changes: 80 additions & 0 deletions build/instancedStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import GroupStage from "./groupStage"

export default class InstancedStage extends GroupStage {
constructor(parent, name, dependencies, instanceFactory) {
super(parent, name, dependencies)
this.subState = `notRunning`
this.instanceFactory = instanceFactory
}

blocksChildren() {
switch (this.subState) {
case `notRunning`:
case `gettingInstanceNames`:
return true

case `waitingForChildren`:
return false

default:
this.criticalStop(`Sub-state "${this.subState}" is not implemented by "blocksChildren"`)
}
}

handle(potentialError, onSuccess) {
if (potentialError) {
this.subState = `notRunning`
}

super.handle(potentialError, onSuccess)
}

performStart() {
this.subState = `gettingInstanceNames`
this.getInstanceNames()
}

getInstanceNames() {
this.criticalStop(`"getInstanceNames" is not implemented`)
}

checkState() {
switch (this.subState) {
case `notRunning`:
case `waitingForChildren`:
super.checkState()
break

case `gettingInstanceNames`:
break

default:
this.criticalStop(`Sub-state "${this.subState}" is not implemented by "checkState".`)
}
}

gotInstanceNames(instanceNames) {
if (this.subState != `gettingInstanceNames`) {
this.criticalStop(`Sub-state "${this.subState}" is not implemented by "gotInstanceNames".`)
}

this.subState = `waitingForChildren`

this.children
.forEach(child => instanceNames.indexOf(child.name) == -1 && child.stop())

// This removes duplicates.
instanceNames
.forEach(instanceName => this.get([instanceName]) || this.instanceFactory(instanceName))

super.performStart()
}

done() {
if (this.subState != `waitingForChildren`) {
this.criticalStop(`Sub-state "${this.subState}" is not implemented by "done".`)
}

super.done()
}
}
41 changes: 41 additions & 0 deletions build/javaScriptCombineStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
import * as uglifyJs from "uglify-js"
import Stage from "./stage"

export default class JavaScriptCombineStage extends Stage {
constructor(parent, name, dependencies, allParsedFactory) {
super(parent, name, dependencies)
this.allParsedFactory = allParsedFactory
}

performStart() {
const cloned = this
.allParsedFactory()
.map(parsed => parsed.clone(true))

const combined = cloned[0]

cloned
.slice(1)
.forEach(parsed => {
combined.body = combined.body.concat(parsed.body)
combined.end = parsed.end
})

const minified = uglifyJs.minify(combined, {
mangle: {
toplevel: true,
properties: true
},
toplevel: true,
output: {
ast: false,
code: true
}
})

this.handle(minified.error, () => {
this.code = minified.code
this.done()
})
}
}
49 changes: 49 additions & 0 deletions build/javaScriptParseStage.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import * as uglifyJs from "uglify-js"
import FileSearchStage from "./fileSearchStage"
import GroupStage from "./groupStage"
import Stage from "./stage"
import ReadTextStage from "./readTextStage"

class ParseStage extends Stage {
constructor(parent, name, dependencies, sourceFactory, onParse) {
super(parent, name, dependencies)
this.sourceFactory = sourceFactory
this.onParse = onParse
}

performStart() {
const parsed = uglifyJs.minify(this.sourceFactory(), {
parse: {},
compress: false,
mangle: false,
output: {
ast: true,
code: false
}
})
this.handle(parsed.error, () => {
this.onParse(parsed.ast)
this.done()
})
}
}

class InstanceStage extends GroupStage {
constructor(parent, name, dependencies) {
super(parent, name, dependencies)
const readText = new ReadTextStage(this, `read`, [], () => [name])
new ParseStage(this, `parse`, [readText], () => readText.text, parsed => this.parent.parsed[this.name] = parsed)
}

stop() {
super.stop()
delete this.parent.parsed[this.name]
}
}

export default class JavaScriptParseStage extends FileSearchStage {
constructor(parent, name, dependencies, searchPathFactory) {
super(parent, name, dependencies, instanceName => new InstanceStage(this, instanceName, []), searchPathFactory, `js`)
this.parsed = {}
}
}
3 changes: 3 additions & 0 deletions build/oneOff.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import RootStage from "./rootStage"

new RootStage(null, `root`, [], true)
Loading

0 comments on commit 5a814c2

Please sign in to comment.