Skip to content

Commit d39bfca

Browse files
committed
refactor
1 parent 5dc946a commit d39bfca

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

60 files changed

+1949
-7
lines changed

.gitignore

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
node_modules
2-
test
32
tslint.json
43
tsconfig.json
5-
**/*.ts
6-
*.ts
74
src/*.md
85
dist/cr.json
96
coderoad.json

.npmignore

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
src
2+
test
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

cli.js renamed to lib/cli.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,4 @@
11
#! /usr/bin/env node
2-
32
"use strict";
43
var program = require('commander');
54
var chalk = require('chalk');
File renamed without changes.

src/create/validate.js renamed to lib/create/validate.js

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
"use strict";
22
var chalk = require('chalk');
33
var validateNpm = require("validate-npm-package-name");
4-
var process = require('process');
54
var _ = require('lodash');
65
function validatePackageName(name) {
76
var validated = validateNpm(name);
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.

package.json

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "coderoad-cli",
3-
"version": "0.3.15",
3+
"version": "0.3.16",
44
"description": "Command line interface for CodeRoad. Build project files.",
55
"keywords": [
66
"coderoad"
@@ -15,8 +15,14 @@
1515
"license": "ISC",
1616
"preferGlobal": true,
1717
"bin": {
18-
"coderoad": "cli.js"
18+
"coderoad": "lib/cli.js"
1919
},
20+
"files": [
21+
"lib",
22+
"package.json",
23+
"README.md",
24+
"LICENSE.md"
25+
],
2026
"dependencies": {
2127
"chalk": "1.1.1",
2228
"commander": "2.9.0",

src/build/build.ts

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
import * as fs from 'fs';
2+
import * as process from 'process';
3+
import * as validate from './validators';
4+
import {project} from './parser/project';
5+
import {createReadme} from './readme';
6+
import {cleanup} from './parser/cleanup';
7+
8+
// function loadImport(result, index, line: string) {
9+
// let pathToFile = trimQuotes(Match.project(line));
10+
// console.log('import called', pathToFile);
11+
// let lines = fs.readFileSync(pathToFile, 'utf8').split('\n');
12+
// let resultImport = chapter(result, lines, index);
13+
// console.log(resultImport)
14+
// return result;
15+
// }
16+
17+
function build(lines: string[]) {
18+
let result = {
19+
project: {},
20+
chapters: []
21+
}
22+
let index = {
23+
chapter: -1,
24+
page: -1,
25+
task: -1
26+
};
27+
return project(result, lines, index);
28+
}
29+
30+
export default function(filePath: string, output = './coderoad.json') {
31+
// VALIDATE: path name
32+
validate.filePath(filePath);
33+
34+
// Read
35+
let lines: string[] = fs.readFileSync(filePath, 'utf8').split('\n');
36+
// Build
37+
let result = cleanup(build(lines));
38+
39+
if (validate.result(result)) {
40+
// Safe to Write coderoad.json
41+
fs.writeFileSync(output, result, 'utf8');
42+
}
43+
44+
createReadme();
45+
46+
47+
}

src/build/parser/actions.ts

Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
import {trimQuotes, trimLeadingSpaces} from './cleanup';
2+
import * as Match from './match';
3+
import {brackets} from './brackets';
4+
5+
export function trimCommandValue(text: string): string {
6+
let value = text.substring(text.indexOf('(') + 1).slice(0, -1);
7+
let command = {
8+
action: text.substring(0, text.indexOf('(')),
9+
value: trimLeadingSpaces(trimQuotes(value))
10+
};
11+
return command.action + '(\'' + command.value + '\')';
12+
}
13+
14+
function doAction(type: 'test' | 'hint', isArray, actionValue, result, line, index) {
15+
// set to array
16+
if (result.chapters[index.chapter].pages[index.page].tasks[index.task][type] === undefined) {
17+
result.chapters[index.chapter].pages[index.page].tasks[index.task][type] = [];
18+
}
19+
if (!!isArray) {
20+
let valueList: string[] = actionValue.slice(1, -1).split(',');
21+
valueList.forEach((value) => {
22+
value = trimQuotes(value.trim(), true);
23+
result.chapters[index.chapter].pages[index.page].tasks[index.task][type].push(value);
24+
})
25+
} else {
26+
result.chapters[index.chapter].pages[index.page].tasks[index.task][type].push(actionValue);
27+
}
28+
return result;
29+
}
30+
31+
export function addToTasks(result, line, index): result {
32+
let match = Match.isAction(line);
33+
let action = match.action; // action|test|hint|answer
34+
let task = result.chapters[index.chapter].pages[index.page].tasks[index.task];
35+
let trimmedContent = line.slice(action.length + 2, line.length - 1); // content between brackets
36+
let actionValue: string = trimQuotes(trimmedContent);
37+
let isActionArray = Match.isArray(trimQuotes(actionValue));
38+
switch (action) {
39+
case 'test':
40+
result = doAction('tests', isActionArray, actionValue, result, line, index);
41+
break;
42+
case 'hint':
43+
result = doAction('hints', isActionArray, actionValue, result, line, index);
44+
break;
45+
case 'action':
46+
if (task.actions === undefined) {
47+
result.chapters[index.chapter].pages[index.page].tasks[index.task].actions = [];
48+
}
49+
if (!!isActionArray) {
50+
var arrayOfActions = JSON.parse(isActionArray);
51+
arrayOfActions.forEach(function(value) {
52+
let value = trimCommandValue(trimQuotes(value.trim()));
53+
result.chapters[index.chapter].pages[index.page].tasks[index.task].actions.push(value);
54+
});
55+
}
56+
else {
57+
let value = trimCommandValue(actionValue);
58+
result.chapters[index.chapter].pages[index.page].tasks[index.task].actions.push(value);
59+
}
60+
return result;
61+
break;
62+
63+
default:
64+
console.log('Invalid task action');
65+
}
66+
return result;
67+
}

src/build/parser/chapter.ts

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
import * as Match from './match';
2+
import {page} from './page';
3+
import {loadImport} from './import';
4+
import {trimLineBreaks} from './cleanup';
5+
6+
export function chapter(result: Result, lines: string[], index: Index): Result {
7+
index.page = -1;
8+
index.chapter += 1;
9+
result.chapters.push({
10+
title: Match.chapter(lines[0]).trim(),
11+
pages: []
12+
});
13+
let inCodeBlock = false;
14+
let i = 0;
15+
while (i < lines.length - 1) {
16+
i += 1;
17+
18+
let line = lines[i];
19+
// import
20+
let importFile = Match.isImport(line);
21+
if (!!importFile) {
22+
lines = loadImport(lines, importFile);
23+
continue;
24+
}
25+
26+
if (!!Match.codeBlock(line)) {
27+
if (line.length > 3) {
28+
result = addToDescription(i, result, line, index);
29+
continue;
30+
}
31+
inCodeBlock = !inCodeBlock;
32+
}
33+
34+
if (!inCodeBlock) {
35+
if (Match.page(line)) {
36+
return page(result, lines.slice(i), index);
37+
} else if (Match.chapter(line) && i > 1) {
38+
return chapter(result, lines.slice(i), index);
39+
} else {
40+
result = addToDescription(i, result, line, index);
41+
}
42+
}
43+
}
44+
return result;
45+
}
46+
47+
function addToDescription(i, result, line, index) {
48+
if (result.chapters[index.chapter].description === undefined) {
49+
result.chapters[index.chapter].description = '';
50+
}
51+
if (i > 1) {
52+
result.chapters[index.chapter].description += '\n';
53+
}
54+
result.chapters[index.chapter].description += line;
55+
return result;
56+
}

src/build/parser/cleanup.ts

Lines changed: 74 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,74 @@
1+
export function trimLineBreaks(text: string) {
2+
if (text.match(/^\s+|\s+$/)) {
3+
text = text.replace(/^[\s\r\n]+|[\s\r\n]+$/g, '');
4+
}
5+
return text;
6+
}
7+
8+
let quotes = ['\'', '"'];
9+
10+
export function trimQuotes(text: string, quotesOnly?: boolean) {
11+
if (!!text.match(/^[\r\n]/)) {
12+
return text;
13+
} else if (!!text.match(/^\s/)) {
14+
return trimQuotes(text.slice(1), quotesOnly)
15+
} else if (!!text.match(/\s$/)) {
16+
// trim trailing spaces
17+
return trimQuotes(text.slice(0, text.length - 1), quotesOnly);
18+
} else if (quotes.indexOf(text.charAt(0)) > -1 &&
19+
quotes.indexOf(text.charAt(text.length - 1)) > -1) {
20+
// trim quotes
21+
return trimQuotes(text.slice(1, text.length - 1), quotesOnly);
22+
} else if (!quotesOnly &&
23+
!!text.match(/^`{3}.+`{3}$/m)) {
24+
// trim ``` ```
25+
return trimQuotes(text.slice(3, text.length - 3), quotesOnly);
26+
} else if (!quotesOnly &&
27+
text.charAt(0) === '`' &&
28+
text.charAt(text.length - 1) === '`') {
29+
// trim string literals
30+
return trimQuotes(text.slice(1, text.length - 1), quotesOnly);
31+
} else {
32+
return text;
33+
}
34+
}
35+
36+
export function trimLeadingSpaces(text: string) {
37+
if (!!text.match(/^\s/)) {
38+
return text.slice(1);
39+
} else {
40+
return text;
41+
}
42+
}
43+
44+
export function cleanup(result: Result) {
45+
result = JSON.parse(JSON.stringify(result));
46+
if (result.project.description) {
47+
result.project.description = trimLineBreaks(result.project.description);
48+
}
49+
if (result.chapters) {
50+
result.chapters.map((chapter) => {
51+
if (chapter.description) {
52+
chapter.description = trimLineBreaks(chapter.description);
53+
}
54+
if (chapter.pages) {
55+
chapter.pages.map((page) => {
56+
if (page.description) {
57+
page.description = trimLineBreaks(page.description);
58+
}
59+
if (page.explanation) {
60+
page.explanation = trimLineBreaks(page.explanation);
61+
}
62+
if (page.tasks) {
63+
page.tasks.map((task) => {
64+
if (task.description) {
65+
task.description = trimLineBreaks(task.description);
66+
}
67+
});
68+
}
69+
});
70+
}
71+
});
72+
}
73+
return JSON.stringify(result, null, 2);
74+
}

src/build/parser/import.ts

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as fs from 'fs';
2+
import * as path from 'path';
3+
import {fileExists} from '../../tools/file';
4+
import {trimQuotes} from './cleanup';
5+
6+
export function loadImport(lines: string[], pathToMd: string): string[] {
7+
pathToMd = trimQuotes(pathToMd);
8+
if (!pathToMd.match(/\.md$/)) {
9+
pathToMd = pathToMd.concat('.md');
10+
}
11+
let realPath = path.join(process.cwd(), pathToMd);
12+
if (!fileExists(pathToMd)) {
13+
console.log('Invalid path to markdown file', realPath);
14+
return;
15+
}
16+
let importLines: string = fs.readFileSync(realPath, 'utf8');
17+
let splitLines = importLines.split('\n');
18+
return lines.concat(splitLines);
19+
}

src/build/parser/match.ts

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
function match(char: string, times: number) {
2+
return new RegExp('^' + char + '{' + times + '}(?!#)(.*?)$', 'gm');
3+
}
4+
5+
var regex = {
6+
'#': match('#', 1),
7+
'##': match('#', 2),
8+
'###': match('#', 3),
9+
'+': match('\\+', 1),
10+
'```': match('`', 3)
11+
};
12+
13+
function parseWithCode(code: string) {
14+
return function(line: string) {
15+
if (!line) {
16+
return false;
17+
}
18+
if (line.match(regex[code])) {
19+
return regex[code].exec(line)[1];
20+
} else {
21+
return false;
22+
}
23+
};
24+
}
25+
26+
export function isEmpty(line: string): boolean {
27+
return !line.length || !!line.match(/^\s+?[\n\r]/);
28+
}
29+
30+
export const project = parseWithCode('#');
31+
export const chapter = parseWithCode('##');
32+
export const page = parseWithCode('###');
33+
export const task = parseWithCode('+');
34+
export const codeBlock = parseWithCode('```');
35+
export const isArray = function(line: string) {
36+
let isMatch = line.match(/^\[.+\]$/);
37+
return isMatch ? isMatch[0] : false;
38+
}
39+
export const isAction = function(line: string) {
40+
// returns [0]: full, [1]: action, [2]: target
41+
let match = line.match(/^@(action|test|hint)/);
42+
return !!match ? {
43+
action: match[1]
44+
// content: match[2]
45+
} : false;
46+
}
47+
export const isImport = function(line: string) {
48+
let isMatch = line.match(/^@import\((.+)\)$/);
49+
return isMatch ? isMatch[1] : false;
50+
}

0 commit comments

Comments
 (0)