...
20 |
21 |
22 |
23 |
24 |
45 |
46 |
47 |
48 | `
49 |
--------------------------------------------------------------------------------
/codegen/makefile.go:
--------------------------------------------------------------------------------
1 | package codegen
2 |
3 | // Makefile returns the Makefile code for the new app
4 | //
5 | // TODO: this won't be necessary after https://github.com/factorapp/factor/issues/11
6 | const Makefile = `wasm:
7 | GOARCH=wasm GOOS=js ~/gowasm/bin/go build -o example.wasm ./client
8 | mv example.wasm ./app/
9 |
10 | run: wasm
11 | cd server && go build && cd .. && ./server/server`
12 |
--------------------------------------------------------------------------------
/codegen/routes.go:
--------------------------------------------------------------------------------
1 | package codegen
2 |
3 | // RoutesHTML returns the HTML code for the index route
4 | const RoutesHTML = `
5 | ...
16 |
17 |
18 |
19 |
20 |
41 |
42 |
43 |
44 |
--------------------------------------------------------------------------------
/examples/app/wasm_exec.js:
--------------------------------------------------------------------------------
1 | // Copyright 2018 The Go Authors. All rights reserved.
2 | // Use of this source code is governed by a BSD-style
3 | // license that can be found in the LICENSE file.
4 |
5 | (() => {
6 | // Map web browser API and Node.js API to a single common API (preferring web standards over Node.js API).
7 | const isNodeJS = typeof process !== "undefined";
8 | if (isNodeJS) {
9 | global.require = require;
10 | global.fs = require("fs");
11 |
12 | const nodeCrypto = require("crypto");
13 | global.crypto = {
14 | getRandomValues(b) {
15 | nodeCrypto.randomFillSync(b);
16 | },
17 | };
18 |
19 | global.performance = {
20 | now() {
21 | const [sec, nsec] = process.hrtime();
22 | return sec * 1000 + nsec / 1000000;
23 | },
24 | };
25 |
26 | const util = require("util");
27 | global.TextEncoder = util.TextEncoder;
28 | global.TextDecoder = util.TextDecoder;
29 | } else {
30 | window.global = window;
31 |
32 | let outputBuf = "";
33 | global.fs = {
34 | constants: { O_WRONLY: -1, O_RDWR: -1, O_CREAT: -1, O_TRUNC: -1, O_APPEND: -1, O_EXCL: -1, O_NONBLOCK: -1, O_SYNC: -1 }, // unused
35 | writeSync(fd, buf) {
36 | outputBuf += decoder.decode(buf);
37 | const nl = outputBuf.lastIndexOf("\n");
38 | if (nl != -1) {
39 | console.log(outputBuf.substr(0, nl));
40 | outputBuf = outputBuf.substr(nl + 1);
41 | }
42 | return buf.length;
43 | },
44 | openSync(path, flags, mode) {
45 | const err = new Error("not implemented");
46 | err.code = "ENOSYS";
47 | throw err;
48 | },
49 | };
50 | }
51 |
52 | const encoder = new TextEncoder("utf-8");
53 | const decoder = new TextDecoder("utf-8");
54 |
55 | global.Go = class {
56 | constructor() {
57 | this.argv = ["js"];
58 | this.env = {};
59 | this.exit = (code) => {
60 | if (code !== 0) {
61 | console.warn("exit code:", code);
62 | }
63 | };
64 | this._callbackTimeouts = new Map();
65 | this._nextCallbackTimeoutID = 1;
66 |
67 | const mem = () => {
68 | // The buffer may change when requesting more memory.
69 | return new DataView(this._inst.exports.mem.buffer);
70 | }
71 |
72 | const setInt64 = (addr, v) => {
73 | mem().setUint32(addr + 0, v, true);
74 | mem().setUint32(addr + 4, Math.floor(v / 4294967296), true);
75 | }
76 |
77 | const getInt64 = (addr) => {
78 | const low = mem().getUint32(addr + 0, true);
79 | const high = mem().getInt32(addr + 4, true);
80 | return low + high * 4294967296;
81 | }
82 |
83 | const loadValue = (addr) => {
84 | const f = mem().getFloat64(addr, true);
85 | if (!isNaN(f)) {
86 | return f;
87 | }
88 |
89 | const id = mem().getUint32(addr, true);
90 | return this._values[id];
91 | }
92 |
93 | const storeValue = (addr, v) => {
94 | if (typeof v === "number") {
95 | if (isNaN(v)) {
96 | mem().setUint32(addr + 4, 0x7FF80000, true); // NaN
97 | mem().setUint32(addr, 0, true);
98 | return;
99 | }
100 | mem().setFloat64(addr, v, true);
101 | return;
102 | }
103 |
104 | mem().setUint32(addr + 4, 0x7FF80000, true); // NaN
105 |
106 | switch (v) {
107 | case undefined:
108 | mem().setUint32(addr, 1, true);
109 | return;
110 | case null:
111 | mem().setUint32(addr, 2, true);
112 | return;
113 | case true:
114 | mem().setUint32(addr, 3, true);
115 | return;
116 | case false:
117 | mem().setUint32(addr, 4, true);
118 | return;
119 | }
120 |
121 | if (typeof v === "string") {
122 | let ref = this._stringRefs.get(v);
123 | if (ref === undefined) {
124 | ref = this._values.length;
125 | this._values.push(v);
126 | this._stringRefs.set(v, ref);
127 | }
128 | mem().setUint32(addr, ref, true);
129 | return;
130 | }
131 |
132 | if (typeof v === "symbol") {
133 | let ref = this._symbolRefs.get(v);
134 | if (ref === undefined) {
135 | ref = this._values.length;
136 | this._values.push(v);
137 | this._symbolRefs.set(v, ref);
138 | }
139 | mem().setUint32(addr, ref, true);
140 | return;
141 | }
142 |
143 | let ref = v[this._refProp];
144 | if (ref === undefined) {
145 | ref = this._values.length;
146 | this._values.push(v);
147 | v[this._refProp] = ref;
148 | }
149 | mem().setUint32(addr, ref, true);
150 | }
151 |
152 | const loadSlice = (addr) => {
153 | const array = getInt64(addr + 0);
154 | const len = getInt64(addr + 8);
155 | return new Uint8Array(this._inst.exports.mem.buffer, array, len);
156 | }
157 |
158 | const loadSliceOfValues = (addr) => {
159 | const array = getInt64(addr + 0);
160 | const len = getInt64(addr + 8);
161 | const a = new Array(len);
162 | for (let i = 0; i < len; i++) {
163 | a[i] = loadValue(array + i * 8);
164 | }
165 | return a;
166 | }
167 |
168 | const loadString = (addr) => {
169 | const saddr = getInt64(addr + 0);
170 | const len = getInt64(addr + 8);
171 | return decoder.decode(new DataView(this._inst.exports.mem.buffer, saddr, len));
172 | }
173 |
174 | const timeOrigin = Date.now() - performance.now();
175 | this.importObject = {
176 | go: {
177 | // func wasmExit(code int32)
178 | "runtime.wasmExit": (sp) => {
179 | this.exited = true;
180 | this.exit(mem().getInt32(sp + 8, true));
181 | },
182 |
183 | // func wasmWrite(fd uintptr, p unsafe.Pointer, n int32)
184 | "runtime.wasmWrite": (sp) => {
185 | const fd = getInt64(sp + 8);
186 | const p = getInt64(sp + 16);
187 | const n = mem().getInt32(sp + 24, true);
188 | fs.writeSync(fd, new Uint8Array(this._inst.exports.mem.buffer, p, n));
189 | },
190 |
191 | // func nanotime() int64
192 | "runtime.nanotime": (sp) => {
193 | setInt64(sp + 8, (timeOrigin + performance.now()) * 1000000);
194 | },
195 |
196 | // func walltime() (sec int64, nsec int32)
197 | "runtime.walltime": (sp) => {
198 | const msec = (new Date).getTime();
199 | setInt64(sp + 8, msec / 1000);
200 | mem().setInt32(sp + 16, (msec % 1000) * 1000000, true);
201 | },
202 |
203 | // func scheduleCallback(delay int64) int32
204 | "runtime.scheduleCallback": (sp) => {
205 | const id = this._nextCallbackTimeoutID;
206 | this._nextCallbackTimeoutID++;
207 | this._callbackTimeouts.set(id, setTimeout(
208 | () => { this._resolveCallbackPromise(); },
209 | getInt64(sp + 8) + 1, // setTimeout has been seen to fire up to 1 millisecond early
210 | ));
211 | mem().setInt32(sp + 16, id, true);
212 | },
213 |
214 | // func clearScheduledCallback(id int32)
215 | "runtime.clearScheduledCallback": (sp) => {
216 | const id = mem().getInt32(sp + 8, true);
217 | clearTimeout(this._callbackTimeouts.get(id));
218 | this._callbackTimeouts.delete(id);
219 | },
220 |
221 | // func getRandomData(r []byte)
222 | "runtime.getRandomData": (sp) => {
223 | crypto.getRandomValues(loadSlice(sp + 8));
224 | },
225 |
226 | // func stringVal(value string) ref
227 | "syscall/js.stringVal": (sp) => {
228 | storeValue(sp + 24, loadString(sp + 8));
229 | },
230 |
231 | // func valueGet(v ref, p string) ref
232 | "syscall/js.valueGet": (sp) => {
233 | storeValue(sp + 32, Reflect.get(loadValue(sp + 8), loadString(sp + 16)));
234 | },
235 |
236 | // func valueSet(v ref, p string, x ref)
237 | "syscall/js.valueSet": (sp) => {
238 | Reflect.set(loadValue(sp + 8), loadString(sp + 16), loadValue(sp + 32));
239 | },
240 |
241 | // func valueIndex(v ref, i int) ref
242 | "syscall/js.valueIndex": (sp) => {
243 | storeValue(sp + 24, Reflect.get(loadValue(sp + 8), getInt64(sp + 16)));
244 | },
245 |
246 | // valueSetIndex(v ref, i int, x ref)
247 | "syscall/js.valueSetIndex": (sp) => {
248 | Reflect.set(loadValue(sp + 8), getInt64(sp + 16), loadValue(sp + 24));
249 | },
250 |
251 | // func valueCall(v ref, m string, args []ref) (ref, bool)
252 | "syscall/js.valueCall": (sp) => {
253 | try {
254 | const v = loadValue(sp + 8);
255 | const m = Reflect.get(v, loadString(sp + 16));
256 | const args = loadSliceOfValues(sp + 32);
257 | storeValue(sp + 56, Reflect.apply(m, v, args));
258 | mem().setUint8(sp + 64, 1);
259 | } catch (err) {
260 | storeValue(sp + 56, err);
261 | mem().setUint8(sp + 64, 0);
262 | }
263 | },
264 |
265 | // func valueInvoke(v ref, args []ref) (ref, bool)
266 | "syscall/js.valueInvoke": (sp) => {
267 | try {
268 | const v = loadValue(sp + 8);
269 | const args = loadSliceOfValues(sp + 16);
270 | storeValue(sp + 40, Reflect.apply(v, undefined, args));
271 | mem().setUint8(sp + 48, 1);
272 | } catch (err) {
273 | storeValue(sp + 40, err);
274 | mem().setUint8(sp + 48, 0);
275 | }
276 | },
277 |
278 | // func valueNew(v ref, args []ref) (ref, bool)
279 | "syscall/js.valueNew": (sp) => {
280 | try {
281 | const v = loadValue(sp + 8);
282 | const args = loadSliceOfValues(sp + 16);
283 | storeValue(sp + 40, Reflect.construct(v, args));
284 | mem().setUint8(sp + 48, 1);
285 | } catch (err) {
286 | storeValue(sp + 40, err);
287 | mem().setUint8(sp + 48, 0);
288 | }
289 | },
290 |
291 | // func valueLength(v ref) int
292 | "syscall/js.valueLength": (sp) => {
293 | setInt64(sp + 16, parseInt(loadValue(sp + 8).length));
294 | },
295 |
296 | // valuePrepareString(v ref) (ref, int)
297 | "syscall/js.valuePrepareString": (sp) => {
298 | const str = encoder.encode(String(loadValue(sp + 8)));
299 | storeValue(sp + 16, str);
300 | setInt64(sp + 24, str.length);
301 | },
302 |
303 | // valueLoadString(v ref, b []byte)
304 | "syscall/js.valueLoadString": (sp) => {
305 | const str = loadValue(sp + 8);
306 | loadSlice(sp + 16).set(str);
307 | },
308 |
309 | // func valueInstanceOf(v ref, t ref) bool
310 | "syscall/js.valueInstanceOf": (sp) => {
311 | mem().setUint8(sp + 24, loadValue(sp + 8) instanceof loadValue(sp + 16));
312 | },
313 |
314 | "debug": (value) => {
315 | console.log(value);
316 | },
317 | }
318 | };
319 | }
320 |
321 | async run(instance) {
322 | this._inst = instance;
323 | this._values = [ // TODO: garbage collection
324 | NaN,
325 | undefined,
326 | null,
327 | true,
328 | false,
329 | global,
330 | this._inst.exports.mem,
331 | () => { // resolveCallbackPromise
332 | if (this.exited) {
333 | throw new Error("bad callback: Go program has already exited");
334 | }
335 | setTimeout(this._resolveCallbackPromise, 0); // make sure it is asynchronous
336 | },
337 | ];
338 | this._stringRefs = new Map();
339 | this._symbolRefs = new Map();
340 | this._refProp = Symbol();
341 | this.exited = false;
342 |
343 | const mem = new DataView(this._inst.exports.mem.buffer)
344 |
345 | // Pass command line arguments and environment variables to WebAssembly by writing them to the linear memory.
346 | let offset = 4096;
347 |
348 | const strPtr = (str) => {
349 | let ptr = offset;
350 | new Uint8Array(mem.buffer, offset, str.length + 1).set(encoder.encode(str + "\0"));
351 | offset += str.length + (8 - (str.length % 8));
352 | return ptr;
353 | };
354 |
355 | const argc = this.argv.length;
356 |
357 | const argvPtrs = [];
358 | this.argv.forEach((arg) => {
359 | argvPtrs.push(strPtr(arg));
360 | });
361 |
362 | const keys = Object.keys(this.env).sort();
363 | argvPtrs.push(keys.length);
364 | keys.forEach((key) => {
365 | argvPtrs.push(strPtr(`${key}=${this.env[key]}`));
366 | });
367 |
368 | const argv = offset;
369 | argvPtrs.forEach((ptr) => {
370 | mem.setUint32(offset, ptr, true);
371 | mem.setUint32(offset + 4, 0, true);
372 | offset += 8;
373 | });
374 |
375 | while (true) {
376 | const callbackPromise = new Promise((resolve) => {
377 | this._resolveCallbackPromise = resolve;
378 | });
379 | this._inst.exports.run(argc, argv);
380 | if (this.exited) {
381 | break;
382 | }
383 | await callbackPromise;
384 | }
385 | }
386 | }
387 |
388 | if (isNodeJS) {
389 | if (process.argv.length < 3) {
390 | process.stderr.write("usage: go_js_wasm_exec [wasm binary] [arguments]\n");
391 | process.exit(1);
392 | }
393 |
394 | const go = new Go();
395 | go.argv = process.argv.slice(2);
396 | go.env = process.env;
397 | go.exit = process.exit;
398 | WebAssembly.instantiate(fs.readFileSync(process.argv[2]), go.importObject).then((result) => {
399 | process.on("exit", () => { // Node.js exits if no callback is pending
400 | if (!go.exited) {
401 | console.error("error: all goroutines asleep and no JavaScript callback pending - deadlock!");
402 | process.exit(1);
403 | }
404 | });
405 | return go.run(result.instance);
406 | }).catch((err) => {
407 | console.error(err);
408 | go.exited = true;
409 | process.exit(1);
410 | });
411 | }
412 | })();
413 |
--------------------------------------------------------------------------------
/examples/assets/favicon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/factorapp/factor/69dfdd227a1d960661c62d2546a6126d995b311b/examples/assets/favicon.png
--------------------------------------------------------------------------------
/examples/assets/global.css:
--------------------------------------------------------------------------------
1 | body {
2 | padding-top: 5rem;
3 | }
4 | .starter-template {
5 | padding: 3rem 1.5rem;
6 | text-align: center;
7 | }
8 |
--------------------------------------------------------------------------------
/examples/assets/great-success.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/factorapp/factor/69dfdd227a1d960661c62d2546a6126d995b311b/examples/assets/great-success.png
--------------------------------------------------------------------------------
/examples/assets/manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | "background_color": "#ffffff",
3 | "theme_color": "#aa1e1e",
4 | "name": "TODO",
5 | "short_name": "TODO",
6 | "display": "minimal-ui",
7 | "start_url": "/",
8 | "icons": [
9 | {
10 | "src": "svelte-logo-192.png",
11 | "sizes": "192x192",
12 | "type": "image/png"
13 | },
14 | {
15 | "src": "svelte-logo-512.png",
16 | "sizes": "512x512",
17 | "type": "image/png"
18 | }
19 | ]
20 | }
21 |
--------------------------------------------------------------------------------
/examples/assets/svelte-logo-192.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/factorapp/factor/69dfdd227a1d960661c62d2546a6126d995b311b/examples/assets/svelte-logo-192.png
--------------------------------------------------------------------------------
/examples/assets/svelte-logo-512.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/factorapp/factor/69dfdd227a1d960661c62d2546a6126d995b311b/examples/assets/svelte-logo-512.png
--------------------------------------------------------------------------------
/examples/client/main.go:
--------------------------------------------------------------------------------
1 | // build +js,wasm
2 | package main
3 |
4 | import (
5 | "github.com/factorapp/factor/examples/routes"
6 | "github.com/gowasm/router"
7 | "github.com/gowasm/vecty"
8 | )
9 |
10 | func main() {
11 | c := make(chan struct{}, 0)
12 | // Create a new Router object
13 | r := router.New()
14 | //r.ShouldInterceptLinks = true
15 | // Use HandleFunc to add routes.
16 | r.HandleFunc("/", func(context *router.Context) {
17 |
18 | // The handler for this route simply grabs the name parameter
19 | // from the map of params and says hello.
20 | vecty.SetTitle("Factor: Home")
21 | vecty.RenderBody(&routes.Index{})
22 | })
23 | r.HandleFunc("/about", func(context *router.Context) {
24 |
25 | // The handler for this route simply grabs the name parameter
26 | // from the map of params and says hello.
27 | vecty.SetTitle("Factor: About")
28 | vecty.RenderBody(&routes.About{})
29 | })
30 | // You must call Start in order to start listening for changes
31 | // in the url and trigger the appropriate handler function.
32 | r.Start()
33 | <-c
34 | }
35 |
--------------------------------------------------------------------------------
/examples/components/App.html:
--------------------------------------------------------------------------------
1 |
17 | {header}
18 | {content}
19 | {asAnArg(
help I'm caught in the web
)}
20 |
21 | }
22 |
--------------------------------------------------------------------------------
/examples/routes/about.html:
--------------------------------------------------------------------------------
1 | This is the 'about' page. There's not much here.
4 |
--------------------------------------------------------------------------------
/examples/routes/about_generated.go:
--------------------------------------------------------------------------------
1 | // This file was created with https://github.com/factorapp/factor
2 | // using https://jsgo.io/dave/html2vecty
3 | package routes
4 |
5 | import (
6 | "github.com/gowasm/vecty"
7 | "github.com/gowasm/vecty/elem"
8 | )
9 |
10 | type About struct {
11 | vecty.Core
12 | }
13 |
14 | func (p *About) Render() vecty.ComponentOrHTML {
15 | return elem.Body(
16 | elem.Heading1(
17 | vecty.Text("About this site"),
18 | ),
19 | elem.Paragraph(
20 | vecty.Text("This is the 'about' page. There's not much here."),
21 | ),
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/examples/routes/blog.[slug].html:
--------------------------------------------------------------------------------
1 |
2 | I'm not sure exactly how this'll work yet. Haven't run factor dev against it yet...
5 |
--------------------------------------------------------------------------------
/examples/routes/todoid_generated.go:
--------------------------------------------------------------------------------
1 | // This file was created with https://github.com/factorapp/factor
2 | // using https://jsgo.io/dave/html2vecty
3 | package routes
4 |
5 | import (
6 | "github.com/gowasm/vecty"
7 | "github.com/gowasm/vecty/elem"
8 | )
9 |
10 | type Todoid struct {
11 | vecty.Core
12 | }
13 |
14 | func (p *Todoid) Render() vecty.ComponentOrHTML {
15 | return elem.Body(
16 | elem.Heading1(
17 | vecty.Text("Todo"),
18 | ),
19 | elem.Paragraph(
20 | vecty.Text("I'm not sure exactly how this'll work yet. Haven't run factor dev against it yet..."),
21 | ),
22 | )
23 | }
24 |
--------------------------------------------------------------------------------
/examples/routes/todos.html:
--------------------------------------------------------------------------------
1 |
2 | {{ range .Todos }}
3 |
7 | {{ .TodoComponent . }}
8 | {{ end }}
9 |
10 |
--------------------------------------------------------------------------------
/examples/routes/todos_generated.go:
--------------------------------------------------------------------------------
1 | // This file was created with https://github.com/factorapp/factor
2 | // using https://jsgo.io/dave/html2vecty
3 | package routes
4 |
5 | import (
6 | "github.com/gowasm/vecty"
7 | "github.com/gowasm/vecty/elem"
8 | )
9 |
10 | type Todos struct {
11 | vecty.Core
12 | }
13 |
14 | func (p *Todos) Render() vecty.ComponentOrHTML {
15 | return elem.Body(
16 | elem.Div(
17 | vecty.Markup(
18 | vecty.Class("todos"),
19 | ),
20 | vecty.Text("{{ range .Todos }}"),
21 | vecty.Text("{{ .TodoComponent . }}\n {{ end }}"),
22 | ),
23 | )
24 | }
25 |
--------------------------------------------------------------------------------
/examples/server/main.go:
--------------------------------------------------------------------------------
1 | // build !js,wasm
2 | package main
3 |
4 | import (
5 | "log"
6 | "net/http"
7 |
8 | "github.com/gorilla/rpc/v2"
9 | "github.com/gorilla/rpc/v2/json"
10 |
11 | "github.com/factorapp/factor/examples/models"
12 | )
13 |
14 | func wasmHandler(w http.ResponseWriter, r *http.Request) {
15 | w.Header().Set("Content-Type", "application/wasm")
16 | http.ServeFile(w, r, "./app/example.wasm")
17 | }
18 | func main() {
19 | s := rpc.NewServer()
20 | s.RegisterCodec(json.NewCodec(), "application/json")
21 | s.RegisterCodec(json.NewCodec(), "application/json;charset=UTF-8")
22 | tds := new(models.TodoServer)
23 | s.RegisterService(tds, "TodoServer")
24 | http.HandleFunc("/app/example.wasm", wasmHandler)
25 | http.Handle("/rpc", s)
26 | /* cwd, err := os.Getcwd()
27 | if err != nil {
28 | panic(err)
29 | }
30 | app := filepath.Join(cwd, "app")
31 | */
32 |
33 | http.HandleFunc("/wasm_exec.js", jsHandler)
34 |
35 | http.Handle("/assets/", http.StripPrefix("/assets/", http.FileServer(http.Dir("./assets"))))
36 | http.HandleFunc("/", indexHandler)
37 | log.Fatal(http.ListenAndServe(":3000", nil))
38 | }
39 | func jsHandler(w http.ResponseWriter, r *http.Request) {
40 | http.ServeFile(w, r, "./app/wasm_exec.js")
41 | }
42 |
43 | func indexHandler(w http.ResponseWriter, r *http.Request) {
44 | http.ServeFile(w, r, "./app/index.html")
45 | }
46 |
--------------------------------------------------------------------------------
/files/file_types.go:
--------------------------------------------------------------------------------
1 | package files
2 |
3 | import (
4 | "os"
5 | "path/filepath"
6 | "regexp"
7 | "strings"
8 | )
9 |
10 | var reg = regexp.MustCompile("[^a-zA-Z0-9]+")
11 |
12 | func IsHTML(info os.FileInfo) bool {
13 | return filepath.Ext(info.Name()) == ".html" || filepath.Ext(info.Name()) == ".ghtml"
14 | }
15 |
16 | func GeneratedGoFileName(base, name string) string {
17 | return filepath.Join(base, strings.ToLower(name)+"_generated.go")
18 | }
19 | func ComponentName(path string) string {
20 | base := filepath.Base(path)
21 | base = strings.Replace(base, filepath.Ext(path), "", -1)
22 | return strings.Title(reg.ReplaceAllString(base, ""))
23 | }
24 |
25 | func RouteName(path string) string {
26 | return ComponentName(path)
27 | }
28 |
--------------------------------------------------------------------------------
/files/files.go:
--------------------------------------------------------------------------------
1 | package files
2 |
3 | import (
4 | "path/filepath"
5 | "regexp"
6 | "strings"
7 | )
8 |
9 | var fileNameRegexp = regexp.MustCompile("[^a-zA-Z0-9]+")
10 |
11 | func SanitizedName(path string) string {
12 | base := filepath.Base(path)
13 | base = strings.Replace(base, filepath.Ext(path), "", -1)
14 | return strings.Title(fileNameRegexp.ReplaceAllString(base, ""))
15 | }
16 |
17 | func GeneratedName(path string) string {
18 | n := SanitizedName(path)
19 | return n + "_generated.go"
20 | }
21 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/factorapp/factor
2 |
3 | require (
4 | github.com/BurntSushi/toml v0.3.0 // indirect
5 | github.com/aymerick/douceur v0.2.0
6 | github.com/dave/jennifer v1.0.2
7 | github.com/davecgh/go-spew v1.1.0 // indirect
8 | github.com/fsnotify/fsnotify v1.4.7 // indirect
9 | github.com/go-humble/detect v0.1.2 // indirect
10 | github.com/go-humble/router v0.5.0 // indirect
11 | github.com/gobuffalo/envy v1.6.3
12 | github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f // indirect
13 | github.com/gopherjs/gopherwasm v0.0.0-20180715123310-0765e455aaf6 // indirect
14 | github.com/gopherjs/vecty v0.0.0-20180525005238-a3bd138280bf
15 | github.com/gorilla/css v1.0.0 // indirect
16 | github.com/gorilla/rpc v1.1.0
17 | github.com/gowasm/router v0.5.0
18 | github.com/gowasm/vecty v0.0.0-20180708230244-0151905da086
19 | github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce // indirect
20 | github.com/inconshreveable/mousetrap v1.0.0 // indirect
21 | github.com/joho/godotenv v1.2.0 // indirect
22 | github.com/kr/pretty v0.1.0 // indirect
23 | github.com/magiconair/properties v1.8.0 // indirect
24 | github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9
25 | github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 // indirect
26 | github.com/pelletier/go-toml v1.2.0 // indirect
27 | github.com/pkg/errors v0.8.0
28 | github.com/pmezard/go-difflib v1.0.0 // indirect
29 | github.com/satori/go.uuid v1.2.0
30 | github.com/spf13/afero v1.1.1 // indirect
31 | github.com/spf13/cast v1.2.0 // indirect
32 | github.com/spf13/cobra v0.0.3
33 | github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec // indirect
34 | github.com/spf13/pflag v1.0.1 // indirect
35 | github.com/spf13/viper v1.0.2
36 | github.com/stretchr/testify v1.2.2 // indirect
37 | github.com/tdewolff/buffer v1.1.0 // indirect
38 | github.com/tdewolff/parse v0.0.0-20180607042416-d739d6fccb09
39 | github.com/tdewolff/test v0.0.0-20171106182207-265427085153 // indirect
40 | golang.org/x/sys v0.0.0-20180715085529-ac767d655b30 // indirect
41 | golang.org/x/text v0.3.0 // indirect
42 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 // indirect
43 | gopkg.in/yaml.v2 v2.2.1 // indirect
44 | honnef.co/go/js/dom v0.0.0-20180323154144-6da835bec70f // indirect
45 | )
46 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/BurntSushi/toml v0.3.0 h1:e1/Ivsx3Z0FVTV0NSOv/aVgbUWyQuzj7DDnFblkRvsY=
2 | github.com/BurntSushi/toml v0.3.0/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
3 | github.com/aymerick/douceur v0.2.0 h1:Mv+mAeH1Q+n9Fr+oyamOlAkUNPWPlA8PPGR0QAaYuPk=
4 | github.com/aymerick/douceur v0.2.0/go.mod h1:wlT5vV2O3h55X9m7iVYN0TBM0NH/MmbLnd30/FjWUq4=
5 | github.com/dave/jennifer v1.0.2 h1:ixSwWgh8HCIJN9GlVNvdbKHrD/qfh5Mvd4ZCaFAJbr8=
6 | github.com/dave/jennifer v1.0.2/go.mod h1:fIb+770HOpJ2fmN9EPPKOqm1vMGhB+TwXKMZhrIygKg=
7 | github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
8 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
9 | github.com/fsnotify/fsnotify v1.4.7 h1:IXs+QLmnXW2CcXuY+8Mzv/fWEsPGWxqefPtCP5CnV9I=
10 | github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
11 | github.com/go-humble/detect v0.1.2 h1:mitrDomAvFJiH2D4LNwlRDj5d/3woOATAxKfIsoMP0I=
12 | github.com/go-humble/detect v0.1.2/go.mod h1:pVrYDy9oO+niVnRn94ljzppsRGpVs0l2+xTDW6u1nAQ=
13 | github.com/go-humble/router v0.5.0 h1:tVzuDGj2b8PDjyT/x/saIxgir+qfRdFGEkLuhpAECbU=
14 | github.com/go-humble/router v0.5.0/go.mod h1:skbvzVQASVDieX3rvgRsHnd7oe6JBr98BxAPeheHioM=
15 | github.com/gobuffalo/envy v1.6.3 h1:I9iyNACF0Tovfta7iqLrUAXFHYBDBWveQrjpEv2XeWs=
16 | github.com/gobuffalo/envy v1.6.3/go.mod h1:gOxUQY+OEwqH1a2m25Sqax1GIhj31tPNOIdFzj8QThs=
17 | github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f h1:FDM3EtwZLyhW48YRiyqjivNlNZjAObv4xt4NnJaU+NQ=
18 | github.com/gopherjs/gopherjs v0.0.0-20180628210949-0892b62f0d9f/go.mod h1:wJfORRmW1u3UXTncJ5qlYoELFm8eSnnEO6hX4iZ3EWY=
19 | github.com/gopherjs/gopherwasm v0.0.0-20180715123310-0765e455aaf6 h1:jlhhn5+kvyFKC+diq6kYPdVgkemQMVea3cRRUSGRZBw=
20 | github.com/gopherjs/gopherwasm v0.0.0-20180715123310-0765e455aaf6/go.mod h1:p5NWZjUtSXtYIzAvkf5nz1PKQ1ZP2kf/mb4apd1qN24=
21 | github.com/gopherjs/vecty v0.0.0-20180525005238-a3bd138280bf h1:J7WjKCdpxMhXDzFPP6d41YonnCnPi1JkYOFxIPoH1E0=
22 | github.com/gopherjs/vecty v0.0.0-20180525005238-a3bd138280bf/go.mod h1:5YRDuy7i7SgM0kL+0b5oq+jfDTqPyzV3TQbcDkBvcoE=
23 | github.com/gorilla/css v1.0.0 h1:BQqNyPTi50JCFMTw/b67hByjMVXZRwGha6wxVGkeihY=
24 | github.com/gorilla/css v1.0.0/go.mod h1:Dn721qIggHpt4+EFCcTLTU/vk5ySda2ReITrtgBl60c=
25 | github.com/gorilla/rpc v1.1.0 h1:marKfvVP0Gpd/jHlVBKCQ8RAoUPdX7K1Nuh6l1BNh7A=
26 | github.com/gorilla/rpc v1.1.0/go.mod h1:V4h9r+4sF5HnzqbwIez0fKSpANP0zlYd3qR7p36jkTQ=
27 | github.com/gowasm/router v0.5.0 h1:y77vpo2QRJGpi/a072YIHw+wdAI0U4M9z0UCznW7mxs=
28 | github.com/gowasm/router v0.5.0/go.mod h1:QxUQWSLbwUpnH+eYpsp4L/cmuxMjK93YOePIyd2tav4=
29 | github.com/gowasm/vecty v0.0.0-20180708230244-0151905da086 h1:yCAW35FtKqarvfMi/1uAdTs47la6HtNF7Onl6w1+A8Q=
30 | github.com/gowasm/vecty v0.0.0-20180708230244-0151905da086/go.mod h1:otcQ+kDw3rABBAlJh8bQ1+OtW2kjmJzXo2cXYyFtkj4=
31 | github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce h1:xdsDDbiBDQTKASoGEZ+pEmF1OnWuu8AQ9I8iNbHNeno=
32 | github.com/hashicorp/hcl v0.0.0-20180404174102-ef8a98b0bbce/go.mod h1:oZtUIOe8dh44I2q6ScRibXws4Ajl+d+nod3AaR9vL5w=
33 | github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
34 | github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
35 | github.com/joho/godotenv v1.2.0 h1:vGTvz69FzUFp+X4/bAkb0j5BoLC+9bpqTWY8mjhA9pc=
36 | github.com/joho/godotenv v1.2.0/go.mod h1:7hK45KPybAkOC6peb+G5yklZfMxEjkZhHbwpqxOKXbg=
37 | github.com/kr/pretty v0.1.0 h1:L/CwN0zerZDmRFUapSPitk6f+Q3+0za1rQkzVuMiMFI=
38 | github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo=
39 | github.com/kr/pty v1.1.1/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
40 | github.com/kr/text v0.1.0 h1:45sCR5RtlFHMR4UwH9sdQ5TC8v0qDQCHnXt+kaKSTVE=
41 | github.com/kr/text v0.1.0/go.mod h1:4Jbv+DJW3UT/LiOwJeYQe1efqtUx/iVham/4vfdArNI=
42 | github.com/magiconair/properties v1.8.0 h1:LLgXmsheXeRoUOBOjtwPQCWIYqM/LU1ayDtDePerRcY=
43 | github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
44 | github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9 h1:Y94YB7jrsihrbGSqRNMwRWJ2/dCxr0hdC2oPRohkx0A=
45 | github.com/mitchellh/go-homedir v0.0.0-20180523094522-3864e76763d9/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
46 | github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699 h1:KXZJFdun9knAVAR8tg/aHJEr5DgtcbqyvzacK+CDCaI=
47 | github.com/mitchellh/mapstructure v0.0.0-20180715050151-f15292f7a699/go.mod h1:FVVH3fgwuzCH5S8UJGiWEs2h04kUh9fWfEaFds41c1Y=
48 | github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc=
49 | github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic=
50 | github.com/pkg/errors v0.8.0 h1:WdK/asTD0HN+q6hsWO3/vpuAkAr+tw6aNJNDFFf0+qw=
51 | github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
52 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
53 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
54 | github.com/satori/go.uuid v1.2.0 h1:0uYX9dsZ2yD7q2RtLRtPSdGDWzjeM3TbMJP9utgA0ww=
55 | github.com/satori/go.uuid v1.2.0/go.mod h1:dA0hQrYB0VpLJoorglMZABFdXlWrHn1NEOzdhQKdks0=
56 | github.com/spf13/afero v1.1.1 h1:Lt3ihYMlE+lreX1GS4Qw4ZsNpYQLxIXKBTEOXm3nt6I=
57 | github.com/spf13/afero v1.1.1/go.mod h1:j4pytiNVoe2o6bmDsKpLACNPDBIoEAkihy7loJ1B0CQ=
58 | github.com/spf13/cast v1.2.0 h1:HHl1DSRbEQN2i8tJmtS6ViPyHx35+p51amrdsiTCrkg=
59 | github.com/spf13/cast v1.2.0/go.mod h1:r2rcYCSwa1IExKTDiTfzaxqT2FNHs8hODu4LnUfgKEg=
60 | github.com/spf13/cobra v0.0.3 h1:ZlrZ4XsMRm04Fr5pSFxBgfND2EBVa1nLpiy1stUsX/8=
61 | github.com/spf13/cobra v0.0.3/go.mod h1:1l0Ry5zgKvJasoi3XT1TypsSe7PqH0Sj9dhYf7v3XqQ=
62 | github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec h1:2ZXvIUGghLpdTVHR1UfvfrzoVlZaE/yOWC5LueIHZig=
63 | github.com/spf13/jwalterweatherman v0.0.0-20180109140146-7c0cea34c8ec/go.mod h1:cQK4TGJAtQXfYWX+Ddv3mKDzgVb68N+wFjFa4jdeBTo=
64 | github.com/spf13/pflag v1.0.1 h1:aCvUg6QPl3ibpQUxyLkrEkCHtPqYJL4x9AuhqVqFis4=
65 | github.com/spf13/pflag v1.0.1/go.mod h1:DYY7MBk1bdzusC3SYhjObp+wFpr4gzcvqqNjLnInEg4=
66 | github.com/spf13/viper v1.0.2 h1:Ncr3ZIuJn322w2k1qmzXDnkLAdQMlJqBa9kfAH+irso=
67 | github.com/spf13/viper v1.0.2/go.mod h1:A8kyI5cUJhb8N+3pkfONlcEcZbueH6nhAm0Fq7SrnBM=
68 | github.com/stretchr/testify v1.2.2 h1:bSDNvY7ZPG5RlJ8otE/7V6gMiyenm9RtJ7IUVIAoJ1w=
69 | github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
70 | github.com/tdewolff/buffer v1.1.0 h1:NpNuBqn78WinZjWlJvr0SvCiNKOObr+bIC6VL7QZZIE=
71 | github.com/tdewolff/buffer v1.1.0/go.mod h1:2aVmbfts5BiODsIH0lFp+FX5BiqjSS7P7+cKqieIAb0=
72 | github.com/tdewolff/parse v0.0.0-20180607042416-d739d6fccb09 h1:pHcEtlNu1Bf5P32lo80YMtGn5FewhZXbfE45EhlBe7s=
73 | github.com/tdewolff/parse v0.0.0-20180607042416-d739d6fccb09/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
74 | github.com/tdewolff/parse v1.1.0 h1:tMjj9GCK8zzwjWyxdZ4pabzdWO1VG+G3bvCnG6aUIyQ=
75 | github.com/tdewolff/parse v1.1.0/go.mod h1:8oBwCsVmUkgHO8M5iCzSIDtpzXOT0WXX9cWhz+bIzJQ=
76 | github.com/tdewolff/test v0.0.0-20171106182207-265427085153 h1:B1Z2txQ2QI9nsWELeEvGBAdNhMylGMSCCypjsLJh/Mw=
77 | github.com/tdewolff/test v0.0.0-20171106182207-265427085153/go.mod h1:DiQUlutnqlEvdvhSn2LPGy4TFwRauAaYDsL+683RNX4=
78 | golang.org/x/sys v0.0.0-20180715085529-ac767d655b30 h1:4bYUqrXBoiI7UFQeibUwFhvcHfaEeL75O3lOcZa964o=
79 | golang.org/x/sys v0.0.0-20180715085529-ac767d655b30/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
80 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
81 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
82 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
83 | gopkg.in/check.v1 v1.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
84 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127 h1:qIbj1fsPNlZgppZ+VLlY7N33q108Sa+fhmuc+sWQYwY=
85 | gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
86 | gopkg.in/yaml.v2 v2.2.1 h1:mUhvW9EsL+naU5Q3cakzfE91YhliOondGd6ZrsDBHQE=
87 | gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
88 | honnef.co/go/js/dom v0.0.0-20180323154144-6da835bec70f h1:8wvPTUK0UjW0bwvb0Q2mdhzSf78P6dXQI6mE9v+jJvI=
89 | honnef.co/go/js/dom v0.0.0-20180323154144-6da835bec70f/go.mod h1:sUMDUKNB2ZcVjt92UnLy3cdGs+wDAcrPdV3JP6sVgA4=
90 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | // Copyright © 2018 NAME HERE