1
0
mirror of https://github.com/JuanCanham/HackMyResume.git synced 2025-05-11 08:17:07 +01:00

Compare commits

...

496 Commits

Author SHA1 Message Date
58b6ad841e Remove unused "main" entry from package.json.
Clients who were require()ing hackmyresume should now require()
hackmycore instead.
2016-01-24 17:15:32 -05:00
fc937e3ec8 Update "hackmyapi" references. 2016-01-24 17:14:53 -05:00
8652c7ecdf Rename & bump hackmyapi dependency. 2016-01-24 17:04:01 -05:00
c882235eff Bump version to 1.7.0. 2016-01-24 17:03:35 -05:00
d6c5239f9e Update roadmap.
Use linkable section headings for easier referencing.
2016-01-24 16:56:38 -05:00
4b2db3f720 Introduce dev roadmap. 2016-01-24 16:11:56 -05:00
9736777828 Fix Travis. 2016-01-24 10:53:32 -05:00
d3194fba19 Relocate internal sources to HackMyAPI.
Move internal sources and related tests to:

https://github.com/hacksalot/HackMyAPI
2016-01-24 09:55:04 -05:00
fa29f9794d Update README image. 2016-01-24 06:23:15 -05:00
07915002bb Adjust "merging X onto Y" output. 2016-01-24 05:35:07 -05:00
fbcc06dcda Merge branch 'master' of https://github.com/hacksalot/HackMyResume 2016-01-24 05:17:24 -05:00
7413a3a257 Fixed type 2016-01-24 05:17:02 -05:00
e6d2255291 Scrub. 2016-01-23 23:30:48 -05:00
2840ec3f87 Introduce {{fontSize}} helper. 2016-01-23 22:40:33 -05:00
05cd863ebf Add PDF engines to man page. 2016-01-23 20:30:23 -05:00
20961afb62 Introduce {{color}} helper. 2016-01-23 20:24:35 -05:00
1256095e25 Support "fonts.all" in FRESH themes.
Add support for default font specs in FRESH theme.json files. The "all"
format matches any format that doesn't have a specific key in "fonts".
2016-01-23 03:58:11 -05:00
f073c79b8d Better dynamic font handling. 2016-01-22 22:19:28 -05:00
ac9e4aa1a0 Capture CHANGELOG and FAQ. 2016-01-22 20:00:07 -05:00
915f35b1e6 Improve Underscore.js rendering support. 2016-01-22 10:36:26 -05:00
4fe74057f9 Improve font helpers.
Log a warning on incorrect use.
2016-01-22 08:33:01 -05:00
5a1ec033bb Adjust USE.txt.
--opts has changed to --options and --no-tips to --tips.
2016-01-22 08:27:21 -05:00
6801e39f97 Tweak output colorization. 2016-01-22 04:55:29 -05:00
f6f383751f Fix JSON Resume theme rendering glitch. 2016-01-22 03:05:41 -05:00
43ed564a6e Disable tips and theme messages by default.
Instead of displaying tips by default and allowing users to turn them
off with --no-tips, hide tips by default and allow users to show them
with --tips.
2016-01-22 02:51:00 -05:00
7b3364c356 Document parameter. 2016-01-22 02:44:17 -05:00
58a7fc09e5 Add toUpper helper. 2016-01-22 02:44:04 -05:00
01c053702d Gather. 2016-01-21 23:40:15 -05:00
a935fe7dc2 Introduce {{fontFace}} helper. 2016-01-21 23:39:30 -05:00
c9825fa016 Update .gitignore. 2016-01-21 23:23:24 -05:00
9eb9207348 Mention string.prototype.endswith dependency. 2016-01-21 05:41:39 -05:00
6b171e69db Improve CSS handling. 2016-01-21 05:21:49 -05:00
5b0ee89e34 Bump FRESCA to 0.6.0. 2016-01-20 21:44:01 -05:00
8bd3ddc7fd Bump fresh-resume-starter to 0.2.2. 2016-01-20 21:43:49 -05:00
984ae95576 Cleanup. 2016-01-20 21:43:11 -05:00
f77cced7f3 Improve error handling. 2016-01-20 19:59:36 -05:00
57787f1bc7 Re-introduce API-level tests. 2016-01-20 01:48:57 -05:00
9419f905df Build verb invocation should return JSON result. 2016-01-20 01:48:33 -05:00
001fd893c1 Add tests for partial resume loading (FRESH). 2016-01-20 00:21:07 -05:00
babe4b4b31 Bump versions. 2016-01-20 00:20:10 -05:00
201f39442e Add support for .ignore flag in FRESH and JRS resumes.
Preliminary support for ".ignore" on any non-leaf FRESH or JRS node.
Nodes (employment entries, education stints, etc.) decorated with
".ignore" will be treated by HMR as if they weren't present.
2016-01-19 20:09:59 -05:00
47f6aff561 Improve keyword regex.
Better support for simple keywords like "C" or "R".
2016-01-19 19:10:20 -05:00
cef9a92cb6 Introduce CHANGELOG.md. 2016-01-19 16:42:48 -05:00
2253e4ead7 Fix theme counts.
The N in "Applying theme FOOBAR (N formats)" should reflect the count of
explicit + freebie output formats.
2016-01-19 16:01:34 -05:00
2f628f8564 Reconnect process exit codes. 2016-01-18 20:06:45 -05:00
23cd52885b Swallow inline failures in CONVERT. 2016-01-18 19:21:25 -05:00
181419ae28 Improve PEEK command behavior. 2016-01-18 19:20:17 -05:00
a81ad0fef2 Tweak build command error condition. 2016-01-18 18:36:24 -05:00
d220cedfeb Improve behavior of PEEK command. 2016-01-18 18:35:38 -05:00
e72564162b Remove custom "extend" method.
Replace with NPM extend.
2016-01-18 17:31:08 -05:00
c98d05270e Improve error handling. 2016-01-18 17:13:37 -05:00
3e3803ed85 Improve error handling. 2016-01-18 14:10:35 -05:00
c8d8e566f8 Add IIFE. 2016-01-18 14:10:25 -05:00
712cba57b8 Capture. 2016-01-18 00:34:57 -05:00
c9e45d4991 Capture. 2016-01-17 21:46:58 -05:00
e9edc0d15c Bump fresh-test-resumes to 0.5.0. 2016-01-16 16:34:25 -05:00
b99a09c88a Integrate tests with new fresh-jrs-converter structure. 2016-01-16 16:29:00 -05:00
5c95fe7af1 Integrate with fresh-jrs-converter.
Move FRESH/JRS conversion logic (and all future format conversions) into
a separate repo.
2016-01-16 12:40:16 -05:00
17f2ebb753 Modularize messages.
...and move strings out of error.js.
2016-01-15 23:46:43 -05:00
fc67f680ee Move output messages to YAML. 2016-01-15 22:52:10 -05:00
88879257e6 Document PEEK command.
Add preliminary docs around PEEK.
2016-01-15 14:46:13 -05:00
fff45e1431 Update tests. 2016-01-15 13:36:57 -05:00
934d8a6123 Update --options file loading. 2016-01-15 13:36:20 -05:00
defe9b6e95 Remove magic number. 2016-01-15 13:35:45 -05:00
4c5ccc001a Introduce PEEK command.
Peek at arbitrary resumes and resume objects paths with "hackmyresume
peek <resume> [objectPath]". For ex:

hackmyresume PEEK resume.json
hackmyresume PEEK resume.json info
hackmyresume PEEK resume.json employment[2].keywords
hackmyresume PEEK r1.json r2.json r3.json info.brief
2016-01-15 13:08:01 -05:00
de5c2ecb95 Update dependencies. 2016-01-14 23:39:54 -05:00
dbb95aef3a Bump fresh-resume-starter / fresh-test-resumes. 2016-01-14 15:53:03 -05:00
c9ae2ffef3 Improve errors / tests consistency. 2016-01-14 14:22:26 -05:00
86af2a2c4f Rename test-cli.js to test-api.js. 2016-01-14 12:07:43 -05:00
37ea6cf804 Rename error-handler.js to error.js. 2016-01-14 11:49:27 -05:00
a9c685c6a4 Refactor error handling (interim). 2016-01-14 11:47:05 -05:00
7765e85336 Integrate printf(). 2016-01-14 09:46:29 -05:00
7af50c51f6 Gather. 2016-01-14 08:48:07 -05:00
19b30d55ec Move error handling out of core. 2016-01-13 15:28:02 -05:00
eddda8146e Bump FRESCA and fresh-themes versions. 2016-01-13 14:46:35 -05:00
1a0b91a58f Update conversion tests. 2016-01-12 18:14:06 -05:00
1b94ada709 Misc improvements. 2016-01-12 18:13:54 -05:00
1966b0a862 Move string transformation out of FRESHResume. 2016-01-12 13:28:20 -05:00
8ced6a730a Fix BUILD command event notifications. 2016-01-12 12:46:55 -05:00
6cd1e60e79 Sort projects. 2016-01-12 12:46:18 -05:00
be691e4230 Remove commented lines. 2016-01-12 12:46:05 -05:00
07b23109f9 Use async spawn() by default. 2016-01-12 12:32:32 -05:00
32769a2b0b Update license to 2016. 2016-01-11 21:16:11 -05:00
280977cb62 Update package.json contributors. 2016-01-11 21:16:01 -05:00
ddceec68a2 Improve --options tests. 2016-01-11 21:15:28 -05:00
b961fd1c07 Fix global leak. 2016-01-11 21:14:40 -05:00
342b960f63 Add tests for raw JSON and file via --options / -o. 2016-01-11 20:52:17 -05:00
f965bf456a Fix JSON file loading glitch with --options. 2016-01-11 20:52:07 -05:00
69be38110f Update license notice in index.js. 2016-01-11 19:56:44 -05:00
3800e19418 Process TXT global partials. 2016-01-11 19:56:19 -05:00
e29ed58a1c Tests: Update theme name. 2016-01-11 18:08:31 -05:00
11bfcd4bef Support raw JSON in the --options parameter. 2016-01-11 18:07:56 -05:00
fbc2e9a4db Bump version to 1.6.0. 2016-01-11 14:04:05 -05:00
7814786957 Recruit Markdown partials when present. 2016-01-11 12:36:00 -05:00
542776fd2e Add shortcut options to man page. 2016-01-11 08:31:05 -05:00
815ee3dc7e Support lowercase -v version flag.
Commander.js built-in version handling uses an uppercase shortcut (-V)
for the version, so the common -v (lowercase) isn't recognized and
errors out.
2016-01-11 08:29:46 -05:00
376e720f4b Scrub. 2016-01-11 08:21:06 -05:00
b224c8939b Remove redundant conditional. 2016-01-11 08:20:48 -05:00
0ecac98cff Remove totally unnecessary line.
Totally.
2016-01-10 19:11:43 -05:00
1416f57d0b Move verb.js to /verbs folder. 2016-01-10 19:08:29 -05:00
65c7e41c53 Remove unused var. 2016-01-10 19:02:24 -05:00
c8cc673ad5 Update man page. 2016-01-10 18:48:57 -05:00
656dbe2fc2 Capture. 2016-01-10 14:53:22 -05:00
a4ee7127ee Fix stack reporting glitch. 2016-01-10 13:28:20 -05:00
fee21a7b17 Always use JSONLint for SyntaxError post-processing.
Remove the check for SyntaxError's built-in line and character
indicators and always re-parse on error to grab the line/column.
2016-01-10 05:17:28 -05:00
32fd8dc636 Merge pull request #102 from beeryt/master
Fixed typo
2016-01-10 02:27:03 -05:00
2c8f444d42 Fixed type 2016-01-09 21:12:19 -08:00
bd8b587c5b Remove explicit logger and error handler params. 2016-01-09 22:34:21 -05:00
4c954b79df Scrub. 2016-01-09 22:15:50 -05:00
b7fffbcf73 Update helper reference in analysis .hbs. 2016-01-09 22:14:34 -05:00
0829800b65 Move helpers to /helpers. 2016-01-09 22:13:29 -05:00
d7cfc76636 Promote console helpers has to console-helpers.js. 2016-01-09 22:11:06 -05:00
311030474d Tests: Remove hard-coded version number. 2016-01-09 20:29:30 -05:00
ec69e668ff Bump version to 1.5.3. 2016-01-09 20:21:17 -05:00
f18910f490 Generate ANALYZE console output from Handlebars template. 2016-01-09 20:18:56 -05:00
540ad48d61 Scrub. 2016-01-09 16:56:30 -05:00
540c745069 Exclude Emacs cruft. 2016-01-09 16:44:00 -05:00
c5b8eec33a Move CLI-related assets to subfolder. 2016-01-09 16:14:28 -05:00
bece335a64 Fix CREATE verb output. 2016-01-09 15:58:39 -05:00
3aabb5028d Continue moving logging out of core. 2016-01-09 15:49:08 -05:00
732bc9809a Start moving logging out of core. 2016-01-09 13:58:47 -05:00
d77b484e55 Verbs are event emitters.
Let verbs source events through EventEmitter. Using aggregation is a bit
simpler here than extending because of the Resig "Class" stuff.
2016-01-09 08:12:55 -05:00
43564bf380 Update tests. 2016-01-09 06:44:47 -05:00
88c71f6e9c Move commands to Verb hierarchy
Move flat command functions (BUILD, ANALYZE, etc.) to a shallow Verb
hierarchy. Allow command verbs to inherit common functionality and prep
for better debugging/logging as well as test mocks.
2016-01-09 06:44:22 -05:00
47e8605f50 Handle args in mock/passthrough case. 2016-01-09 05:30:12 -05:00
9466a8c0dd Remove spawn-watch.
No longer necessary.
2016-01-09 05:29:45 -05:00
d878270bc6 Encapsulate CLI interface to ease testing.
Strip index.js down to its bare essentials, move primary logic to
main.js, and expose the latter via module.exports. This allows tests to
execute the same code path(s) HMR runs in production.
2016-01-08 19:22:44 -05:00
3b38c4818f Bump version. 2016-01-08 18:56:07 -05:00
62c967526f Fix PDF exception glitch. 2016-01-08 18:15:12 -05:00
6e5a44798b Update README. 2016-01-08 16:36:19 -05:00
1fbfe2507b Carry over debug flag. 2016-01-08 16:33:13 -05:00
d6a3aab68a Make Handlebars options explicit. 2016-01-08 16:27:19 -05:00
9fdfd1b5a6 Add baseline support for -d or --debug flag.
For now, -d just force-emits the stack when there is one. In the future,
it can trigger more detailed logging info.
2016-01-08 16:08:33 -05:00
f4e763bd9c Merge branch 'master' of https://github.com/hacksalot/HackMyResume 2016-01-08 12:28:45 -05:00
fbfff2a4e4 load theme partials for non html and doc
load global partials for html and doc only but load theme partials for
all outputs
2016-01-08 12:28:23 -05:00
1c93932737 Fix jsHint error. 2016-01-08 12:24:23 -05:00
cba29511bc Analyze: fix coverage percentage glitch. 2016-01-08 12:20:51 -05:00
1d655a4ddb Support duration units for JRS resumes. 2016-01-08 12:13:54 -05:00
ca94513630 Fix single format output error.
Fixes #97.
2016-01-08 11:59:10 -05:00
971d4a5439 Update FAQ and README. 2016-01-08 11:48:10 -05:00
f3dcbd9081 Improve error vs. warning formatting.
Errors = red. Warnings = yellow.
2016-01-08 10:42:24 -05:00
29c53af843 Rename "invalidTarget" error to "invalidFormat". 2016-01-08 10:09:46 -05:00
8d24087faa Rename src/gen --> src/generators. 2016-01-08 10:02:47 -05:00
95df8e5af4 Rename src/eng --> src/renderers
A renderer is a thing that renders or "paints" an arbitrary format using
a templating engine like Handlebars or Underscore. A generator is a
thing responsible for generating a given output format like HTML or MS
Word.
2016-01-08 09:59:47 -05:00
8a1da777b0 Bump version to 1.5.0. 2016-01-08 09:38:53 -05:00
44555da00f Fix PNG output format for JSON Resume themes. 2016-01-08 09:36:32 -05:00
46bd5d51cc Support implicit PDF generation (interim). 2016-01-08 09:00:43 -05:00
3964d300aa Update README. 2016-01-08 08:59:43 -05:00
d6280e6d89 Start integrating JRS and FRESH rendering paths. 2016-01-08 08:40:19 -05:00
4a2a47f551 Tweak casing. 2016-01-08 07:08:12 -05:00
ae51930c9c Tweak indentation. 2016-01-08 07:06:26 -05:00
fb33455bea Refactor JRS rendering. 2016-01-08 06:48:04 -05:00
28c703daf7 Improve error handling: PDFs. 2016-01-08 05:11:38 -05:00
0246a5da19 Remove html-pdf-generator class.
PDF generation now performed via html-pdf-cli-generator.
2016-01-07 18:34:43 -05:00
840d17c67b Wrap rasterize.js in IIFE / satisfy jsHint. 2016-01-07 18:33:26 -05:00
9f22e94cf7 Merge pull request #95 from aruberto/partials-fix
load theme partials for non html and doc
2016-01-07 18:30:54 -05:00
97ebecd84a Support CLI-based PDF generation.
Support Phantom and wkhtmltopdf generation via CLI.
2016-01-07 18:24:25 -05:00
96b9bb68e3 Introduce Phantom.js rasterizer script.
Via
https://raw.githubusercontent.com/ariya/phantomjs/master/examples/rasterize.js.
2016-01-07 17:53:42 -05:00
c5a5d3761d Remove explicit Phantom and wkhtmltopdf dependency.
Phantom is too heavy to impose on casual users and wkhtmltopdf errors
out on half the systems out there. We're better off speaking to both
tools, when present, via CLI or a secondary script.
2016-01-07 16:47:59 -05:00
c147403b1c load theme partials for non html and doc
load global partials for html and doc only but load theme partials for
all outputs
2016-01-07 16:39:46 -05:00
a2723452c2 Improve ENOENT handling. 2016-01-07 16:13:09 -05:00
cb3488276d Refactor error handling.
Work towards better debug/log/stack trace options for error cases.
2016-01-07 15:54:10 -05:00
43419c27cf Refactor API surface. 2016-01-07 13:44:39 -05:00
0f0c399dd5 Update CLI tests. 2016-01-07 13:12:21 -05:00
cb46497346 Rename generate.js to build.js.
Should match the canonical verb name -- "build". Generate is an alias.
2016-01-07 12:03:44 -05:00
850c640368 Annotate Phantom gen method. 2016-01-07 10:54:46 -05:00
60e455b36d Emit call stack for wkhtmltopdf errors. 2016-01-07 10:54:27 -05:00
af896c85ea Bump version to 1.4.2. 2016-01-07 02:06:55 -05:00
6a7bb5ea5b Update README. 2016-01-07 01:09:48 -05:00
3b6f2ad37e Introduce FAQ.
Use a separate Markdown document instead of the GH wiki so that the FAQ
is present after clone and advertises itself in the root folder.
2016-01-07 00:58:40 -05:00
101eebdd95 Update tests. 2016-01-06 14:17:27 -05:00
830c36818e Tweak missing file message for "new" command. 2016-01-06 14:15:27 -05:00
39e995213f Improve starter resume.
"hackmyresume new" should emit a starter resume that a) has example
information and b) validates.
2016-01-06 14:09:22 -05:00
37a053722d Update Travis URLs. 2016-01-06 11:36:40 -05:00
12fcf3b0cb Fix package.json glitch. 2016-01-06 11:28:09 -05:00
43ad9c1c71 Merge branch 'master' of https://github.com/hacksalot/HackMyResume 2016-01-06 11:24:02 -05:00
4f9207a868 Fix: formatDate helper references the moment method, not the momentDate object 2016-01-06 11:23:39 -05:00
3d1f589bc1 formatDate helper now will only use moment if date is valid. If it's not, will use the user inputted value or a fallback parameter, if it is provided 2016-01-06 11:23:38 -05:00
ae436a3b84 Scrub. 2016-01-06 11:18:50 -05:00
202bb44c76 Update contributors.
@robertmain @jjanusch
2016-01-06 11:18:31 -05:00
041c609ff0 Merge pull request #85 from jjanusch/feat/present-formatter
formatDate template helper error handling and fallback
2016-01-06 11:00:17 -05:00
712b504168 Support global theme partials (interim). 2016-01-06 10:48:51 -05:00
bc9f0d468f Update tests w/ new validation behavior. 2016-01-06 00:44:46 -05:00
2d20077c08 Support --assert option for validate command.
Cause HMR to return an error code if validation fails and the --assert
option is present.
2016-01-06 00:44:34 -05:00
f61deda4e8 Fix format detection error in validate logic. 2016-01-06 00:21:18 -05:00
8203fa50ae Prep convert.js. 2016-01-06 00:20:30 -05:00
c5eab0fd9c Scrub. 2016-01-05 23:59:41 -05:00
40e71238ac Scrub. 2016-01-05 23:46:01 -05:00
9d75b207d1 Formalize empty-fresh.json dependency. 2016-01-05 23:28:49 -05:00
9b52c396d3 Fix missing method rename. 2016-01-05 22:32:46 -05:00
2759727984 Add convenience method. 2016-01-05 22:26:16 -05:00
e230d640cb Rename imp() to i() (interim). 2016-01-05 22:02:11 -05:00
d69688697c Update README. 2016-01-05 19:48:11 -05:00
9f7ec62b18 Bump fresh-themes to 0.11.0-beta. 2016-01-05 10:26:29 -05:00
b1a02918ff Support --no-tips flag. 2016-01-05 10:10:24 -05:00
ec05f6737a Emit JSON Resume theme instructions. 2016-01-05 10:10:12 -05:00
da5db6477b Introduce --color and --no-color options.
These are handled by Chalk, but need to be registered with Commander.js
in order for Chalk to see them.
2016-01-05 09:42:39 -05:00
0f580efb2b Mention ANALYZE command in man page. 2016-01-05 09:38:42 -05:00
ff23ee508b Restore app title. 2016-01-05 09:38:21 -05:00
2819faeb6f Improve theme/format inheritance (interim). 2016-01-05 09:28:40 -05:00
d205e882f6 Introduce FRESH theme/format inheritance.
Support "inherits" property in theme.json (FRESH themes only).
2016-01-05 06:34:56 -05:00
3f40441b0a Bump FRESCA version to 0.3.0. 2016-01-05 05:09:29 -05:00
6185f20ec9 Sort project history by default. 2016-01-05 05:00:04 -05:00
6a61989eb4 Introduce {{dateRange}} and {{camelCase}} helpers. 2016-01-05 04:59:51 -05:00
d658a069cd Rename {{hasSection}} helper to {{section}}. 2016-01-05 04:59:26 -05:00
25688dbe8e Bump fresh-test-resume to 0.2.1. 2016-01-05 01:41:04 -05:00
98362b9687 Bump fresh-test-resumes version. 2016-01-05 00:04:10 -05:00
4c31c96891 Introduce has/hasSection helpers. 2016-01-05 00:03:54 -05:00
219209c6ca Fix logic glitch in {{sectionTitle}} helper. 2016-01-04 19:46:45 -05:00
eff9fc51cb Integrate fresh-test-resumes module. 2016-01-04 19:45:49 -05:00
2ba23ee80d Add support for user-definable section titles.
Introduce a {{sectionTitle}} helper; requires theme updates.
2016-01-04 16:20:48 -05:00
0f83f8f5c2 Merge remote-tracking branch 'refs/remotes/origin/master' into dev 2016-01-04 16:13:37 -05:00
4ba3a3f2a9 Merge branch 'master' of https://github.com/hacksalot/HackMyResume 2016-01-04 08:09:12 -05:00
db486a48aa Update README. 2016-01-04 08:06:51 -05:00
2cab1195e8 Fix 'create' alias. 2016-01-04 07:25:48 -05:00
ce75f09210 Refactor API interface. 2016-01-04 07:23:20 -05:00
a8fed1b69b Add missing semicolon. 2016-01-04 04:15:13 -05:00
62ca2020d8 Bump FRESH themes version. 2016-01-04 04:15:01 -05:00
f65cf8880e Add support for external options file. 2016-01-04 02:50:00 -05:00
c8d4a3deb3 Handle global options.
Fix broken --silent flag and set up -o/-opts.
2016-01-04 01:49:35 -05:00
d5e2a45034 Output theme message on generate. 2016-01-04 00:58:41 -05:00
2465f2ce1c Fix gap analysis glitches. 2016-01-04 00:14:43 -05:00
f2001bcbb1 Add a couple baseline "analyze" tests. 2016-01-03 23:18:35 -05:00
d5afb3eb2e Handle missing dates during gap inspection. 2016-01-03 23:17:36 -05:00
c711cb7922 Improve sorting. 2016-01-03 23:17:18 -05:00
e45e0316f6 Remove extraneous regex. 2016-01-03 10:07:58 -05:00
08ab512f4c Add overlap analysis. 2016-01-03 09:48:43 -05:00
f2bf09bf96 Allow variable-unit resume duration. 2016-01-03 09:48:22 -05:00
75e2b1c131 Improve keyword acquisition. 2016-01-03 09:48:02 -05:00
0b7ef16a41 Improve accuracy of keyword counts. 2016-01-03 07:36:05 -05:00
247eec396c Fix string iteration filtering glitch. 2016-01-03 07:35:47 -05:00
46c7fa9838 Add baseline keyword analysis. 2016-01-03 06:39:46 -05:00
b3fb2c7130 Scrub. 2016-01-03 05:06:54 -05:00
c3ec3f28bd Introduce section totals inspector. 2016-01-03 05:03:31 -05:00
0a8ee721e8 Allow for multiple PDF engines / support Phantom PDFs.
Start formalizing PDF generation apparatus and support a `--pdf`
parameter allowing the user to specify the flavor of PDF generation.
2016-01-03 04:11:42 -05:00
8d7cf32988 Finish Commander.js integration. 2016-01-03 03:18:56 -05:00
655ecebaa5 Clean up comments. 2016-01-03 02:40:04 -05:00
8fc0fa99d3 Remove unnecessary indirection. 2016-01-03 02:39:43 -05:00
69e8adc1cc Remove 'minimist' dependency. 2016-01-03 02:25:39 -05:00
6b3396e01b Use Commander.js for invocations. 2016-01-03 02:22:26 -05:00
a95b52acd0 Refactor command processing. 2016-01-02 00:15:46 -05:00
47553b6def Fix ICE encoding issues.
Fix issue where @@@@ is appearing in generated resumes.
2016-01-01 20:27:46 -05:00
e4a549ed30 Tests: Add ICE detection test.
ICE is the internal boilerplate we use to freeze/unfreeze themes when
trying to force-feed them Markdown or other formatted data.
2016-01-01 20:26:47 -05:00
dd2148bb92 Fix: formatDate helper references the moment method, not the momentDate object 2016-01-01 18:05:33 -05:00
d8b9d86896 Scrub. 2016-01-01 17:30:57 -05:00
889bd4bfc5 formatDate helper now will only use moment if date is valid. If it's not, will use the user inputted value or a fallback parameter, if it is provided 2016-01-01 17:27:49 -05:00
13fc903b2b Catch JSON syntax errors for all commands.
...and emit line/column info.
2016-01-01 17:20:42 -05:00
8c8dbfed72 Adjust test paths. 2016-01-01 15:06:36 -05:00
2b669cf35c Tweak error handling for cmd params. 2016-01-01 15:06:16 -05:00
5a2d892b85 Scrub error-handler.js. 2016-01-01 14:59:21 -05:00
37a7c318d5 Remove stack trace for ENOENT. 2016-01-01 14:58:56 -05:00
43873efcab Tweak analyze command error. 2016-01-01 14:38:52 -05:00
bb28e5aa8e Support --help option.
Support standard syntax for the HELP command.
2016-01-01 14:38:00 -05:00
c17261cd25 Merge pull request #81 from tjlav5/master
Fix relative theme directory
2016-01-01 13:46:55 -05:00
49e56cc226 Fix relative theme directory
The theme directory assumes it was a child of the HackMyResume module, but NPM3 will actually flatten this out. Following the same logic that the template-generator uses, find the path to the themes using NPMs require method.
2016-01-01 11:27:05 -05:00
84ad6cf356 Add missing chalk references. 2016-01-01 04:57:50 -05:00
b96526da31 Replace chalk with colors in tests. 2016-01-01 04:48:20 -05:00
cb14452df3 Replace colors with chalk.
Chalk has a few more options and doesn't mess around with
String.prototype.
2016-01-01 04:44:14 -05:00
d54b9a6d6c Remove unused method. 2016-01-01 03:45:14 -05:00
6285c2db3b Introduce "analyze" verb and framework.
Introduce a new "analyze" command and start setting up the inspector /
analyzer pipeline with a simple "gap analysis" inspector using a
reference-counted gap detection approach.
2016-01-01 03:39:48 -05:00
3453293c79 Bump version to 1.3.1. 2015-12-31 20:41:54 -05:00
fb32cb0d78 Tests: Bump Johnny's expected duration to 4 years.
Happy New Year, everybody.
2015-12-31 20:00:39 -05:00
baccb75256 Tests: fix Travis error on Node 0.10.
Node 0.10 doesn't have path.parse, so use require('parse-filepath') as a
workaround.
2015-12-31 19:51:06 -05:00
5c39c1c93d Remove extraneous console.log. 2015-12-31 19:47:55 -05:00
48cc315fc8 Update Travis shields.
Add version and a badge for the /dev branch.
2015-12-31 19:17:56 -05:00
ea8da6811a Include Node 0.10 in Travis tests.
We've already done some work to support legacy Node 0.10 (ex
https://github.com/hacksalot/HackMyResume/issues/31#issuecomment-167155845)
no reason to drop this support by omitting tests.
2015-12-31 18:31:39 -05:00
dbda48c16d Add additional validate tests. 2015-12-31 18:24:45 -05:00
bc710b5c6e Merge pull request #77 from hacksalot/dev
v1.3.0 changes
2015-12-31 06:43:34 -05:00
b85d40b1b3 Improve XML encoding for Word docs.
Fix various encoding errors.
2015-12-31 06:38:30 -05:00
069c02ddcc Interim changes supporting v1.3.0. 2015-12-31 03:34:41 -05:00
1f6d77fc28 Bump version to 1.3.0. 2015-12-31 03:18:02 -05:00
2b4266ee42 Merge pull request #69 from zhuangya/missing-extend-def-fix-68
fix: missing extend method
2015-12-30 22:11:20 -05:00
2b3c83c57e Merge branch 'master' of https://github.com/hacksalot/HackMyResume 2015-12-30 22:03:38 -05:00
6f37ccdee3 Merge pull request #75 from hacksalot/dev
Changes for v1.3.0-beta
2015-12-30 21:26:27 -05:00
df27924ac2 Add Johnny Trouble to tests. 2015-12-30 21:07:28 -05:00
3cf24cfb40 Fix PNG generation glitch. 2015-12-30 20:11:21 -05:00
3acf648eb4 Expose helpers to Underscore engine.
Get the same set of helpers working for Underscore and Handlebars
engines. Needs refactoring.
2015-12-30 20:11:09 -05:00
76cafa4249 Fix reference error in explicit themes. 2015-12-30 20:10:14 -05:00
55943bf49a Fix missing semicolon. 2015-12-30 20:09:39 -05:00
a280d8acb2 Support CSS embedding vs. linking. 2015-12-30 19:45:50 -05:00
558a321fe8 Refactor generator logic. 2015-12-30 18:52:41 -05:00
d901047043 Update fluent-themes --> fresh-themes. 2015-12-30 18:50:58 -05:00
d4e0a0fa05 Add {{styleSheet}} helper (placeholder). 2015-12-30 18:19:00 -05:00
22554c61c5 Rename and bump fluent-themes dependency. 2015-12-30 18:18:11 -05:00
72de1bbd33 Scrub. 2015-12-30 15:21:58 -05:00
2ff912e687 Scrub. 2015-12-30 15:11:18 -05:00
ccadb0416f Move freebie formats out of theme class. 2015-12-30 15:03:26 -05:00
5e51beddf7 Refactor. 2015-12-30 14:48:22 -05:00
97c9ba08d0 Fix: Broken HELP command. 2015-12-30 14:00:09 -05:00
39d61c66b9 Finish Theme --> FreshTheme rename. 2015-12-30 13:22:18 -05:00
7a1eadb3fc Tweak error messages.
Stay away from language like "please specify a valid input resume". The
fluentcv fork can use corporate-speak. HackMyResume is more like a
gremlin -- feed it, but never after midnight.
2015-12-30 13:12:51 -05:00
1bcc2f7d0c Add formal support for aliases.
new/create and build/generate
2015-12-30 13:00:30 -05:00
e3cb949992 Fix: Exception when HMR is run without params. 2015-12-30 12:59:21 -05:00
a0c356941c Remove unnecessary line. 2015-12-30 12:44:16 -05:00
3c7868a750 Scrub. 2015-12-30 12:38:01 -05:00
3e7d9c0411 Integrate JRSTheme class. 2015-12-30 12:37:26 -05:00
b21fd93d66 Introduce JRSTheme class.
Start splitting out logic into dedicated abstractions for both FRESH and
JSON Resume themes given the different structure and use cases of each.
2015-12-30 12:08:46 -05:00
37e75acd86 Merge remote-tracking branch 'refs/remotes/origin/master' into dev 2015-12-30 12:06:02 -05:00
6280a18c14 fix: missing extend method
fix #68
2015-12-30 19:20:22 +08:00
5bc8b9c987 Merge remote-tracking branch 'refs/remotes/origin/dev' 2015-12-29 17:58:41 -05:00
0c570f8512 Update README. 2015-12-29 17:43:27 -05:00
7593afa586 Adjust package.json versions.
Relax to v1.3.0-beta and bump fluent-themes version.
2015-12-29 17:33:16 -05:00
417d07f469 Merge pull request #60 from zhuangya/images
Thanks @zhuangya! Nice work.
2015-12-29 17:21:37 -05:00
b803eba934 Scrub string.js.
Will probably be retired in favor of Node reusables.
2015-12-29 10:26:30 -05:00
483207e5a0 Improve Markdown support for JSON Resume themes. 2015-12-29 10:01:45 -05:00
02ef2b2241 Improve error handling.
Better support for spawn errors encountered during generation (for ex,
PDFs through wkhtml) + general refactoring.
2015-12-29 06:35:55 -05:00
13430bcad5 Refactor status codes. 2015-12-29 05:09:05 -05:00
e65c0e128e Fix tests glitch. 2015-12-29 03:50:00 -05:00
bf5c040971 Copy JRS theme assets to target. 2015-12-29 03:10:26 -05:00
5dd3d1a3b4 chore: remove debugging console logs 2015-12-29 03:40:42 +08:00
6b0ea0c7bd add: png format 2015-12-29 03:29:13 +08:00
6bc6b3262e Add tests for FRESH/JRS cross-generation.
Ability to generate JSON Resume themes from FRESH format resumes and
vice-versa.
2015-12-28 04:39:03 -05:00
3c1ae4cbd1 Add baseline support for local generation of JSON Resume themes. 2015-12-28 04:37:42 -05:00
547b87afc6 LINT prior to running tests. 2015-12-28 04:17:48 -05:00
db31744c98 Adjust "npm test" command.
Fix issue with tests being run twice and run tests through Grunt for
LINTing and other pre/post processing.
2015-12-28 04:16:53 -05:00
9423a19842 Remove extraneous references to "tests" plural. 2015-12-28 04:01:30 -05:00
07b303e530 Bump version to 1.3.0. 2015-12-28 03:51:39 -05:00
ec51148374 Introduce interim contribution guidelines. 2015-12-27 00:08:45 -05:00
0514f7805c Add contributors to package.json.
Contributors first, author last.
2015-12-26 22:47:39 -05:00
dfa19899b0 Merge pull request #41 from zhuangya/npm-test
package.json test scripts and travis :)
2015-12-26 21:12:39 -05:00
1265ecab9f chore: remove generated resume, more node ci
- node 0.11 0.12
- remove and ignore `test/sandbox` from git
2015-12-26 19:13:15 +08:00
1ad297ec7a add: missing travis-image url 2015-12-25 07:08:17 +08:00
68628e3304 add: travis yml and badge 2015-12-25 07:06:52 +08:00
1a6d7d5723 change: package.json enhancement
- add `scripts` for something like `npm test` and `npm run grunt`
- add missing devDep: `grunt-cli`
2015-12-25 07:03:16 +08:00
78a8b9c58e Merge pull request #40 from hacksalot/rel/v1.2.2
rel/v1.2.2
2015-12-24 17:44:56 -05:00
5e7abb66bd Safer source format conversions.
Quick fix against missing fields in FRESH and/or JRS (ahead of introing
more robust standalone converter thing). Address portions of #31 and
#33.
2015-12-24 17:51:26 -05:00
358c397bb9 Show call stack on error.
Hat tip @Furchin.
2015-12-24 16:22:29 -05:00
3d41528059 Fix path parsing issue on prev versions of Node.js.
Work around absence of path.parse in Node versions < v0.12. Addresses
#31 and #33.
2015-12-24 16:18:38 -05:00
79637b611a Bump version to 1.2.2. 2015-12-24 16:09:37 -05:00
5de796b119 Merge pull request #35 from hacksalot/rel/v1.2.1
rel/v1.2.1
2015-12-24 07:41:12 -05:00
bf84341acf Version -> 1.2.1
Holding off on 1.3.0 pending HTTP/HTTPS support. #rebase #semver
2015-12-24 07:42:07 -05:00
bbac1fdceb Improve test coverage around incomplete JRS resumes.
Add quick sanity checks around incomplete or irregular JRS-format
resumes. Also decorate existing JRS test resumes (/tests/resumes/) with
current 0.0.0 JRS version ahead of 1.0.0 release.
2015-12-24 06:08:45 -05:00
c5ee1ee33c Quick fix for ".history" errors.
Affects #31 and #33.
2015-12-24 04:05:56 -05:00
c74eda90ed Introduce lodash dependency. 2015-12-24 04:05:17 -05:00
ef2fe95bd8 Remove unused method. 2015-12-24 04:04:44 -05:00
e2589b3730 Fix validate command error.
Still hitting some inconsistent behavior in different NPM
versions/platforms with invalid uppercase dependency names per
https://github.com/npm/npm/issues/3692. Partial fix for #33.
2015-12-24 03:23:56 -05:00
ebad1677bc Replace file-exists.js with NPM path-exists. 2015-12-22 18:55:17 -05:00
dab6ebfd82 Bump fluent-themes version to 0.8.0-beta. 2015-12-22 18:49:29 -05:00
dd61b5360a Update package.json contact info. 2015-12-22 18:49:11 -05:00
fced92a5a0 Bump version to 1.3.0. 2015-12-22 18:47:23 -05:00
64db1a654e Merge pull request #30 from hacksalot/rel/v1.2.0
rel/v1.2.0
2015-12-21 02:57:59 -05:00
31830ee759 Silence tests. 2015-12-21 03:04:40 -05:00
1c05846a4f Add CLI tests. 2015-12-21 02:58:16 -05:00
1db9c2e420 Fix README glitch. 2015-12-21 02:58:01 -05:00
c966f6766c Refactor verbs to separate files. 2015-12-21 02:56:02 -05:00
65b6359fd8 Bump version to 1.2.0. 2015-12-21 00:52:23 -05:00
a54476eede Reaffirm string-based generation.
In recent commits, HackMyResume generation logic, much like the pilots
in Top Gun who became too reliant on air-to-air missiles and lost the
true art of dogfighting, has become dependent on file-based generation
as implicit file assumptions have crept in. This commit reaffirms the
file-less, string-based nature of the generation process and, as a side
effect, adjusts the behavior of (binary) PDF generation to match.
2015-12-21 00:36:08 -05:00
7c0354046c Merge pull request #29 from hacksalot/final/v1.1.0
final/v1.1.0
2015-12-20 20:46:19 -05:00
43cd1c7e52 Allow TO keyword to be omitted.
If the TO keyword is missing, assume the last file passed in is the
destination file.
2015-12-20 20:53:21 -05:00
f80c333361 Scrub. 2015-12-20 20:22:46 -05:00
cdbb264093 Add string convenience method. 2015-12-20 18:42:02 -05:00
87b3bbe785 Bump version to 1.1.0. 2015-12-20 18:41:51 -05:00
b92cf7298a Refactor helpers.
Helpers shouldn't be specific to a given template engine (eg,
Handlebars) in order to allow sharing of helpers between different
template engines. Isolate abstract helpers in another module and apply
them via Handlebars.registerHelper and as necessary for other template
engines.
2015-12-20 18:24:28 -05:00
93456b5f40 Merge pull request #28 from hacksalot/rel-next
rel-next
2015-12-20 15:56:43 -05:00
72f29bf402 Update README. 2015-12-20 15:59:30 -05:00
f6fc384466 Add keywords to package.json. 2015-12-20 15:43:31 -05:00
c5ab3fdfae Bump version to 1.0.1. 2015-12-20 15:43:20 -05:00
78c5081a29 Support Markdown-driven hyperlinks in MS Word. 2015-12-20 15:42:31 -05:00
d0c181ee8c Merge pull request #26 from hacksalot/rel-v1.0.0
v1.0.0
2015-12-19 12:33:19 -05:00
80c6bb6e8b Rename to HMR. 2015-12-19 12:37:42 -05:00
786b3fd3b2 Merge pull request #25 from fluentdesk/v0.11.0
v0.11.0
2015-12-19 08:58:59 -05:00
f0a22be731 Skip underscore-prefixed folders during theme load. 2015-12-19 08:39:36 -05:00
ade60022fd Introduce new helpers. 2015-12-19 00:24:21 -05:00
7daba910ed Bump fluent-themes version. 2015-12-18 17:27:40 -05:00
a016d6d91a Update README. 2015-12-18 17:23:40 -05:00
fcaa97ed35 Change author info. 2015-12-18 15:35:00 -05:00
bb7373a229 Remove duplicate is-my-json-valid dependency. 2015-12-18 15:34:52 -05:00
759dcc30e7 Update tests. 2015-12-18 15:34:30 -05:00
0e47f02a33 Rename tests/jane-doe.json. 2015-12-18 15:34:00 -05:00
5fe90517e7 Auto-clean tests folder. 2015-12-18 15:33:18 -05:00
92128da381 Install URL-trimming helper. 2015-12-18 14:51:51 -05:00
1441fe3ae5 Class-ify Underscore/Handlebars engine. 2015-12-18 13:17:07 -05:00
b0bc71cd66 Introduce "either" helper for Handlebars themes. 2015-12-18 13:00:47 -05:00
e908e8bb34 Add missing Underscore require(). 2015-12-18 10:13:50 -05:00
d708a6c6d8 Refactor Handlebars helpers. 2015-12-18 10:10:30 -05:00
a630741098 Fix exception. 2015-12-18 10:10:19 -05:00
01d148e47c Bump version to 0.11.0. 2015-12-18 10:08:52 -05:00
dbd41ec439 Bump FRESCA version. 2015-12-17 14:28:24 -05:00
fc9cbab974 Choose template engine from active theme. 2015-12-17 11:04:29 -05:00
36f8010ebc Merge pull request #24 from fluentdesk/feat-notices
feat-notices
2015-12-17 10:27:58 -05:00
e80d8fb5c8 Bump version. 2015-12-17 10:29:22 -05:00
eabab26eef Update file headers. 2015-12-17 10:15:59 -05:00
18dbb23168 Merge pull request #23 from fluentdesk/v0.10.2
v0.10.2
2015-12-17 07:52:21 -05:00
9ad2a1e92e Add simple-html-tokenizer dependency. 2015-12-16 23:28:57 -05:00
5475b081b1 Support basic Markdown in MS Word docs. 2015-12-16 23:26:53 -05:00
ae9c295ce1 Better Handlebars support. 2015-12-16 20:13:27 -05:00
e0ef774692 Add missing semicolon. 2015-12-16 12:13:50 -05:00
0c1364593a Support post-save callback. 2015-12-16 11:26:30 -05:00
1603a4bc73 Prosecute FCVD updates. 2015-12-16 11:25:50 -05:00
8361cf9960 Remove extraneous comma.
Go away, extraneous comma.
2015-12-16 11:25:04 -05:00
8273e7d150 ... 2015-12-15 06:20:06 -05:00
5c49a8297f Fix: FCVD error. 2015-12-14 07:32:41 -05:00
ee1e4bf699 Scrub. 2015-12-12 11:13:47 -05:00
f5a8e36e50 Refactor theme tests. 2015-12-12 11:13:37 -05:00
b38a7c1da2 Improve conversions and tests. 2015-12-12 10:48:26 -05:00
fe2247329e Bump version to 0.10.2. 2015-12-12 04:43:29 -05:00
9d459370ce Update FRESH<-->JRS converter. 2015-12-12 04:42:56 -05:00
201d96fe22 Merge pull request #22 from fluentdesk/v0.10.1
v0.10.1
2015-12-11 22:29:58 -05:00
8747429bc6 Update NPM registry description. 2015-12-11 22:35:26 -05:00
95540efe29 Tweak FRESCA and theme dependency versions. 2015-12-11 04:02:05 -05:00
0474dc7dbe Bump version to 0.10.1. 2015-12-11 03:04:34 -05:00
e5af6c38e0 Merge remote-tracking branch 'refs/remotes/origin/master' into v0.10.1 2015-12-11 03:01:31 -05:00
00e6407347 Kludge theme loading issue for FCVD.
Resolve in v0.11.0.
2015-12-10 10:28:19 -05:00
3805a36271 Fix folder generation wrinkle. 2015-12-09 23:30:53 -05:00
a3cefa1c42 Merge pull request #20 from fluentdesk/v0.10.0
v0.10.0
2015-12-09 23:28:25 -05:00
81276cf2cc Update README. 2015-12-09 23:09:33 -05:00
541198321e Fix JSHint warnings. 2015-12-09 21:44:35 -05:00
91aba39050 Add file LINTing through JSHint. 2015-12-09 21:44:18 -05:00
f7a3da0a4d Add generator tests for all themes. 2015-12-09 05:41:04 -05:00
0395792359 Restore canonical output filename. 2015-12-09 05:08:10 -05:00
2abfe4426c Refactor. 2015-12-09 04:32:48 -05:00
3dcf3c3974 Tweak Markdownification. 2015-12-09 04:32:39 -05:00
857de65750 More MEGADESK. 2015-12-09 00:13:58 -05:00
f3c9f92263 Add baseline Markdownification. 2015-12-08 22:22:33 -05:00
e8704e1374 Fix file generation glitch. 2015-12-08 22:22:14 -05:00
87c03b437c Generate safe date times; don't hard-code. 2015-12-08 22:21:42 -05:00
1a757e8a87 Bump FRESCA version to 0.2.0. 2015-12-08 21:12:19 -05:00
7c58f0ea96 Add symlink support. 2015-12-08 10:13:04 -05:00
fcaeb381fe Gather. 2015-12-07 21:24:14 -05:00
5a716dff16 Add basic multiplexing support. 2015-12-07 16:39:59 -05:00
8ee2716245 Scrub theme.js. 2015-12-07 10:16:38 -05:00
5f19f0a7df Add baseline support for multifile themes. #rough 2015-12-07 09:51:00 -05:00
cf25621679 Introduce placeholder LaTeX generator. 2015-12-06 18:29:16 -05:00
228f14d06c Support recursive theme template loading. 2015-12-06 18:19:33 -05:00
307c37dc44 Use "src" subfolder instead of "templates". 2015-12-06 18:18:36 -05:00
3b8d100f39 Add baseline Handlebars support. 2015-12-06 16:19:55 -05:00
fb783cdbc6 Add Handlebars dependency. 2015-12-06 14:57:20 -05:00
e4d098a3ce Add safety for implicit Markdown. 2015-12-06 05:51:03 -05:00
263f224e1b Bump fluent-themes version to 0.6.0-beta. 2015-12-06 05:50:14 -05:00
92ca11f23c Adjust output. 2015-12-02 15:10:38 -05:00
5b3a25c461 Support NEW command. 2015-12-02 14:56:36 -05:00
2431ae4d89 Bump version to 0.10.0. 2015-12-02 14:56:06 -05:00
2a8f0196b4 Update LICENSE author. 2015-12-01 14:33:49 -05:00
JD
d2791014ef Merge pull request #19 from fluentdesk/rel/v0.9.1
Rel/v0.9.1
2015-11-24 10:52:20 -05:00
e51eb270fc Bump versions. 2015-11-24 10:57:04 -05:00
fbc98060ce Fix theme loading glitch. 2015-11-24 10:56:28 -05:00
af33b6eded Merge branch 'refs/heads/feat/fresh' 2015-11-22 03:19:15 -05:00
51a44ce4a8 Merge remote-tracking branch 'refs/remotes/origin/master' into feat/fresh 2015-11-22 03:19:01 -05:00
JD
80315f12ac Merge pull request #18 from fluentdesk/feat/fresh
Feat/fresh
2015-11-22 03:09:31 -05:00
37225aec84 Update FRESCA version. 2015-11-22 03:03:16 -05:00
39fd689f61 Fix README table glitch. 2015-11-22 02:41:21 -05:00
5899989feb Update README. 2015-11-22 02:37:14 -05:00
42770989bc Tweak colors for Linux. 2015-11-22 00:10:08 -05:00
eade6f3a5c Tweak colors. 2015-11-22 00:07:30 -05:00
0fe334f433 Bump fluent-themes version. 2015-11-21 23:28:33 -05:00
5735ddc495 Multiple enhancements.
A set of rough enhancements supporting FRESH:

- Added ability to process multiple sources for all commands (BUILD,
VALIDATE, CONVERT).

- Added new HELP command to show usage.

- Improved error-handling and color-coding.
2015-11-21 16:12:22 -05:00
992069b22d Cleanup. 2015-11-21 10:33:16 -05:00
cbddb4b3aa Add convenience filter for links. 2015-11-21 09:13:21 -05:00
317de75a5b Refactor. 2015-11-21 07:59:30 -05:00
9fbab27d73 Improve validation and color-coding. 2015-11-21 05:56:16 -05:00
e44239b24a Update package.json deps. 2015-11-21 03:11:37 -05:00
debd866545 Adjust date references. 2015-11-21 03:11:18 -05:00
bf34b01367 Add YUIDoc support 2015-11-21 03:10:11 -05:00
5304cbabd9 Tweak converter. 2015-11-20 15:29:38 -05:00
4de997840e Scrub. 2015-11-20 09:53:36 -05:00
9cde39703e Clean up handling of "meta". 2015-11-20 09:28:55 -05:00
ad6d2c75ca Update FRESH tests. 2015-11-20 08:29:28 -05:00
c14176a504 Implement "convert" command. 2015-11-20 08:29:19 -05:00
16cf97e08e Improve converter. 2015-11-20 08:27:39 -05:00
c96d37b7f1 Update README. 2015-11-19 17:43:54 -05:00
15a74587bc Update FRESH tests with new exemplar name. 2015-11-19 17:19:27 -05:00
1b3fdfbc5f Add FRESH validation test. 2015-11-19 16:24:56 -05:00
0c1b1734ee Update tests. 2015-11-19 15:39:26 -05:00
35b9f2b764 Fix JSON date validation.
JSON "date" type should accept YYYY, YYYY-MM, and YYYY-MM-DD but
is-my-json-valid only validates the last of the three.
2015-11-19 12:36:58 -05:00
87618afa8d Remove unused verb. 2015-11-19 10:39:59 -05:00
458c8519b5 Update FRESH tests with recent changes. 2015-11-19 10:39:48 -05:00
0aa9bc2937 Rename Sheet/FreshSheet to JRSResume/FRESHResume. 2015-11-19 10:39:14 -05:00
a410153253 Implement "generate" and "validate" verbs.
Start moving to a more familiar verb-based interface with "generate" and
"validate" commands. Use with "fluentcv generate" or "fluentcv
validate".
2015-11-19 09:46:02 -05:00
9044dff504 Introduce FRESH and JSONResume conversion routines. 2015-11-19 09:39:49 -05:00
b167abcb78 Bump version to 0.9.0. 2015-11-19 01:57:43 -05:00
ce95593031 Relax copyright notices. 2015-11-19 01:57:15 -05:00
30b6bc4d80 Remove invalid object model reference. 2015-11-19 01:47:23 -05:00
0bebd87bd6 Rename JSON Resume test sheet to test-jrs-sheet.js. 2015-11-18 23:44:38 -05:00
f3eb46a154 Add starter tests for FRESH sheet. 2015-11-18 23:44:16 -05:00
6ce2ae2391 Introduce FRESH sheet class.
Introduce the canonical FRESH sheet class based on the old HackMyResume
(HMR) sources. Prepare to replace JSON Resume-specific handling with
generic FRESH handling.
2015-11-18 23:42:09 -05:00
JD
8cb3e8849e Merge pull request #17 from fluentdesk/ver/0.8.0
ver/0.8.0
2015-11-18 04:56:27 -05:00
07a072f8d7 Bump fluent-themes version. 2015-11-18 04:56:49 -05:00
8a56c61d56 Fix HTML-based PDF generator glitch. 2015-11-07 02:46:12 -05:00
99722e3bd1 Add filename metadata on save. 2015-11-06 14:56:33 -05:00
aa0ef4e8a4 Adjust CSS file handling by generators. 2015-11-05 00:57:57 -05:00
537bd4a7b9 Clear profiles on sheet reset. 2015-11-05 00:57:23 -05:00
9bd41d5825 Don't save "display_progress_bar". 2015-11-05 00:57:10 -05:00
4cc3fd3a1f Comments. 2015-11-05 00:56:41 -05:00
4752c3040e Store theme folder reference. 2015-11-05 00:56:06 -05:00
61c7d6b8f9 Bump version to 0.8.0. 2015-11-05 00:54:23 -05:00
89b7ed4d5b Expose FluentDate at API level. 2015-11-05 00:53:48 -05:00
fdfdd970a7 Bump version. 2015-10-27 21:48:30 -04:00
JD
22bb3252cd Merge pull request #15 from fluentdesk/ver/0.8.0
Bump version.
2015-10-27 21:48:28 -04:00
JD
b4907dc1b9 Merge pull request #14 from fluentdesk/ver/0.8.0
ver/0.7.2
2015-10-27 21:37:35 -04:00
2c6436be5e Bump fluent-themes version. 2015-10-27 21:11:00 -04:00
1e44ce5e5e Fix: Allow "current" in addition to "present" and "now". 2015-10-27 21:09:54 -04:00
9de5069c20 Fix: Allow year-only dates ('YYYY'). 2015-10-27 21:07:45 -04:00
b0b2af8278 Fix glitch with prettyifying HTML links. 2015-10-27 20:59:21 -04:00
JD
f9c4a70ca4 Merge pull request #13 from fluentdesk/ver/0.7.1
Ver/0.7.1
2015-10-27 07:35:40 -04:00
1782d06b37 Bump version. 2015-10-27 07:39:06 -04:00
5dee90b8e3 Remove process.exit() call. 2015-10-27 07:37:24 -04:00
54 changed files with 3194 additions and 1934 deletions

38
.gitignore vendored
View File

@ -1,2 +1,40 @@
node_modules/
tests/sandbox/
doc/
docs/
local/
npm-debug.log
# Emacs detritus
# -*- mode: gitignore; -*-
*~
\#*\#
/.emacs.desktop
/.emacs.desktop.lock
*.elc
auto-save-list
tramp
.\#*
# Org-mode
.org-id-locations
*_archive
# flymake-mode
*_flymake.*
# eshell files
/eshell/history
/eshell/lastdir
# elpa packages
/elpa/
# reftex files
*.rel
# AUCTeX auto folder
/auto/
# cask packages
.cask/

9
.travis.yml Normal file
View File

@ -0,0 +1,9 @@
language: node_js
node_js:
- "0.10"
- "0.11"
- "0.12"
- "4.0"
- "4.1"
- "4.2"
- "5.0"

306
CHANGELOG.md Normal file
View File

@ -0,0 +1,306 @@
CHANGELOG
=========
## v1.6.0
### Major Improvements
- Better consistency and coverage for all FRESH resumes and themes ([#45][i45]).
- Initial support for overridable fonts in FRESH themes. Like a particular
theme, but want to change the typography? The specific fonts used by a theme
can now be overridden by the user. (FRESH themes only).
- New resume sections! Support for `projects` and `affiliation` resume sections
for technical and creative projects and memberships / clubs / associations,
respectively ([#92][i92]).
- New command! `PEEK` at any arbitrary field or entry on your `.json` resume.
### Added
- Improved handling of start and end dates on `employment`, `projects`,
`education`, and other sections with start/end dates.
- Support for an `.ignore` property on any FRESH or JSON Resume section or field.
Ignored properties will be treated by HackMyResume as if they weren't present.
- Emit extended status and error info with the `--debug` or `-d` switch.
- The `-o` or `--options` switch can now handle either the path to a **JSON
settings file** or **raw JSON/JavaScript**. Since the JSON double quote syntax
is a bit cumbersome from the command line, HackMyResume accepts regular
JavaScript object literal syntax:
hackmyresume build resume.json -o "{ theme: 'compact', silent: 'true' }"
- Ability to disable sorting of resume sections (employments, projects, etc.)
with the `--no-sort` option. HMR will respect the order of items as they appear
in your resume `.json` file.
- Improvements to the starter resume emitted by `hackmyresume new`.
- Theme Authoring: Annotated the HTML and MS Word (XML) formats of the Modern
theme for FRESH theme authors.
- Theme Authoring: Support for templatized CSS files in FRESH themes. CSS files
are now expanded via Handlebars or Underscore prior to copying to the
destination.
- Added CHANGELOG.md (this file).
### Changed
- Rewrote the HackMyResume man/help page.
- Minor incremental updates to the [FRESCA][fresca] schema.
- PDF generation now uses asynchronous `spawn()` which has better compatibility
with old or boutique versions of Node.js.
- Refactored colors in HackMyResume output. Errors will now display as red,
warnings as yellow, successful operations as green, and informational messages
as cyan.
- Theme messages and usage tips will no longer display during resume generation
by default. Use the `--tips` option to view them.
- The `--no-tips` option (default: false) has been replaced with the `--tips`
option, also defaulting to false.
- Removed the `hello-world` theme from the [prebuilt themes][themes] that ship
with HackMyResume. It can be installed separately from NPM:
```bash
npm install fresh-theme-hello-world
hackmyresume resume.json -t node_modules/fresh-theme-hello-world
```
- sd
### Fixed
- PDF generation issues on older versions of Node.
- Stack traces not being emitted correctly.
- Missing `speaking` section will now appear on generated resumes ([#101][i101]).
- Incomplete `education` details will now appear on generated resumes ([#65][i65]).
- Missing employment end date being interpreted as "employment ends today"
([#84][i84]).
- Merging multiple source resumes during `BUILD` sometimes fails.
- Document `--pdf` flag in README ([#111][i111]).
### Internal
- Logging messages have been moved out of core HackMyResume code ahead of
localization support.
- All HackMyResume console output is described in `msg.yml`.
- Relaxed pure JavaScript requirement. CoffeeScript will now start appearing
in HackMyResume and FluentCV sources!
- Additional tests.
## v1.5.2
### Fixed
- Tweak stack trace under `--debug`.
## v1.5.1
### Added
- Preliminary support for `-d` or `--debug` flag. Forces HackMyResume to emit a stack trace under error conditions.
## v1.5.0
### Added
- HackMyResume now supports **CLI-based generation of PDF formats across multiple engines (Phantom, wkhtmltopdf, etc)**. Instead of talking to these engines over a programmatic API, as in prior versions, HackMyResume 1.5+ speaks to them over the same command-line interface (CLI) you'd use if you were using these tools directly.
- HackMyResume will now (attempt to) **generate a PDF output for JSON Resume themes** (in addition to HTML).
- Minor README and FAQ additions.
### Changed
- **Cleaner, quicker installs**. Installing HackMyResume with `npm install hackmyresume -g` will no longer trigger a lengthy, potentially error-prone install for Phantom.js and/or wkhtmltopdf for PDF support. Instead, users can install these engines externally and HMR will use them when present.
- Minor error handling improvements.
### Fixed
- Fixed an error with generating specific formats with the `BUILD` command (#97).
- Fixed numerous latent/undocumented bugs and glitches.
## v1.4.2
### Added
- Introduced [FAQ](https://github.com/hacksalot/HackMyResume/blob/master/FAQ.md).
- Additional README notes.
## v1.4.1
### Added
- `hackmyresume new` now generates a [valid starter resume with sample data](https://github.com/fluentdesk/fresh-resume-starter).
### Fixed
- Fixed warning message when `hackmyresume new` is run without a filename.
## v1.4.0
### Added
- **"Projects" support**: FRESH resumes and themes can now store and display
open source, commercial, private, personal, and creative projects.
- **New command: ANALYZE**. Inspect your resume for gaps, keyword counts, and other metrics. (Experimental.)
- **Side-by-side PDF generation** with Phantom and wkhtmltopdf. Use the `--pdf` or `-p` flag to pick between `phantom` and `wkhtmltopdf` generation.
- **Disable PDF generation** with the `--pdf none` switch.
- **Inherit formats between themes**. Themes can now inherit formats (Word, HTML, .txt, etc.) from other themes. (FRESH themes only.)
- **Rename resume sections** to different languages or wordings.
- **Specify complex options via external file**. Use with the `-o` or `--opts` option.
- **Disable colors** with the `--no-color` flag.
- **Theme messages and usage tips** instructions will now appear in the default HackMyResume output for the `build` command. Run `hackmyresume build resume.json -t awesome` for an example. Turn off with the `--no-tips` flag.
- **Treat validation errors as warnings** with the `--assert` switch (VALIDATE command only).
### Fixed
- Fixed a minor glitch in the FRESCA schema.
- Fixed encoding issues in the `Highlights` section of certain resumes.
- Fix behavior of `-s` and `--silent` flags.
### Changed
- PDF generation now defaults to Phantom for all platforms, with `wkhtmltopdf`
accessible with `--pdf wkhtmltopdf`.
- Resumes are now validated, by default, prior to generation. This
behavior can be disabled with the `--novalidate` or `--force` switch.
- Syntax errors in source FRESH and JSON Resumes are now captured for all
commands.
- Minor updates to README.
- Most themes now inherit Markdown and Plain Text formats from the **Basis**
theme.
### Internal
- Switched from color to chalk.
- Command invocations now handled through commander.js.
- Improved FRESH theme infrastructure (more partials, more DRY).
## v1.3.1
### Added
- Add additional Travis badges.
### Fixed
- Fix extraneous console log output when generating a FRESH theme to MS Word.
- Fix Travis tests on `dev`.
## v1.3.0
### Added
- **Local generation of JSON Resume themes**. To use a JSON Resume theme, first install it with `npm install jsonresume-theme-[blah]` (most JSON Resume themes are on NPM). Then pass it into HackMyResume via the `-t` parameter:
`hackmyresume BUILD resume.json TO out/somefile.all -t node_modules/jsonresume-theme-classy`
- **Better Markdown support.** HackMyResume will start flowing basic Markdown styles to JSON Resume (HTML) themes. FRESH's existing Markdown support has also been improved.
- **.PNG output formats** will start appearing in themes that declare an HTML output.
- **Tweak CSS embedding / linking via the --css option** (`<style></style>` vs `<link>`). Only works for HTML (or HTML-driven) formats of FRESH themes. Use `--css=link` to link in CSS assets and `--css=embed` to embed the styles in the HTML document. For example `hackmyresume BUILD resume.json TO out/resume.all --css=link`.
- **Improved Handlebars/Underscore helper support** for FRESH themes. Handlebars themes can access helpers via `{{helperName}}`. Underscore themes can access helpers via the `h` object.
### Changed
- **Distinguish between validation errors and syntax errors** when validating a FRESH or JRS resume with `hackmyresume validate <blah>`.
- **Emit line and column info** for syntax errors during validation of FRESH and JRS resumes.
- **FRESH themes now embed CSS into HTML formats by default** so that the HTML resume output doesn't have an external CSS file dependency by default. Users can specify normal linked stylesheets by setting `--css=link`.
- **Renamed fluent-themes repo to fresh-themes** in keeping with the other parts of the project.
### Fixed
- Fix various encoding errors in MS Word outputs.
- Fix assorted FRESH-to-JRS and JRS-to-FRESH conversion glitches.
- Fix error when running HMR with no parameters.
- Other minor fixes.
## v1.3.0-beta
- Numerous changes supporting v1.3.0.
## v1.2.2
### Fixed
- Various in-passing fixes.
## v1.2.1
### Fixed
- Fix `require('FRESCA')` error.
- Fix `.history` and `.map` errors on loading incomplete or empty JRS resumes.
### Added
- Better test coverage of incomplete/empty resumes.
## v1.2.0
### Fixed
- Fixed the `new` command: Generate a new FRESH-format resume with `hackmyresume new resume.json` or a new JSON Resume with `hackmyresume new resume.json -f jrs`.
### Added
- Introduced CLI tests.
## v1.1.0
### Fixed
- MS Word formats: Fixed skill coloring/level bug.
### Changed
- Make the `TO` keyword optional. If no `TO` keyword is specified (for the `build` and `convert` commands), HMR will assume the last file passed in is the desired output file. So these are equivalent:
```shell
hackmyresume BUILD resume.json TO out/resume.all
hackmyresume BUILD resume.json out/resume.all
```
`TO` only needs to be included if you have multipled output files:
```shell
hackmyresume BUILD resume.json TO out1.doc out2.html out3.tex
```
## v1.0.1
### Fixed
- Correctly generate MS Word hyperlinks from Markdown source data.
## v1.0.0
- Initial public 1.0 release.
[i45]: https://github.com/hacksalot/HackMyResume/issues/45
[i65]: https://github.com/hacksalot/HackMyResume/issues/65
[i84]: https://github.com/hacksalot/HackMyResume/issues/84
[i92]: https://github.com/hacksalot/HackMyResume/issues/92
[i101]: https://github.com/hacksalot/HackMyResume/issues/101
[i111]: https://github.com/hacksalot/HackMyResume/issues/111
[fresca]: https://github.com/fluentdesk/FRESCA
[themes]: https://github.com/fluentdesk/fresh-themes

58
CONTRIBUTING.md Normal file
View File

@ -0,0 +1,58 @@
Contributing
============
*Note: HackMyResume is also available as [FluentCV][fcv]. Contributors are
credited in both.*
HackMyResume needs your help! Our contribution workflow is based on [GitHub
Flow][flow] and we respond to all pull requests and issues, usually within 24
hours. HackMyResume has no corporate affiliation and no commercial basis, which
allows the project to maintain a strict user-first policy, rapid development
velocity, and a liberal stance on contributions and exotic functionality in
keeping with the spirit (and name) of the tool.
In short, your code is welcome here.
## How To Contribute
1. Optional: [**open an issue**][iss] identifying the feature or bug you'd like
to implement or fix. This step isn't required — you can start hacking away on
HackMyResume without clearing it with us — but helps avoid duplication of work
and ensures that your changes will be accepted once submitted.
2. **Fork and clone** the HackMyResume project.
3. Ideally, **create a new feature branch** (eg, `feat/new-awesome-feature` or
similar; call it whatever you like) to perform your work in.
4. **Install dependencies** by running `npm install` in the top-level
HackMyResume folder.
5. Make your **commits** as usual.
6. **Verify** your changes locally with `npm test`.
7. **Push** your commits.
7. **Submit a pull request** from your feature branch to the HackMyResume `dev`
branch.
8. We'll typically **respond** within 24 hours.
9. Your awesome changes will be **merged** after verification.
## Project Maintainers
HackMyResume is currently maintained by [hacksalot][ha] with assistance from
[tomheon][th] and our awesome [contributors][awesome]. Please direct all official
or internal inquiries to:
```
admin@hackmyresume.com
```
You can reach hacksalot directly at:
```
hacksalot@indevious.com
```
Thanks! See you out there in the trenches.
[fcv]: https://github.com/fluentdesk/fluentcv
[flow]: https://guides.github.com/introduction/flow/
[iss]: https://github.com/hacksalot/HackMyResume/issues
[ha]: https://github.com/hacksalot
[th]: https://github.com/tomheon
[awesome]: https://github.com/hacksalot/HackMyResume/graphs/contributors

228
FAQ.md Normal file
View File

@ -0,0 +1,228 @@
Frequently Asked Questions (FAQ)
================================
## How do I get started with HackMyResume?
1. Install with NPM: `[sudo] npm install hackmyresume -g`.
2. Create a new resume with: `hackmyresume NEW <resume-name>.json`.
3. Test with `hackmyresume BUILD <resume-name>.json`. Look in the `out/` folder.
4. Play around with different themes with the `-t` or `--theme` parameter.
You can use any [FRESH](https://github.com/fluentdesk/fresh-themes) or
[JSON Resume](https://jsonresume.org/themes) theme. The latter have to be
installed first.
## What is FRESH?
FRESH is the **F**luent **R**esume and **E**mployment **S**ystem for **H**umans.
It's an open-source, user-first workflow, schema, and set of practices for
technical candidates and recruiters.
## What is FRESCA?
The **F**RESH **R**esume and **E**mployment **SC**hem**A**&mdash;an open-source,
JSON-driven schema for resumes, CVs, and other employment artifacts. FRESCA is
the recommended schema/format for FRESH, with optional support for JSON Resume.
## What is JSON Resume?
An [open resume standard](http://jsonresume.org/themes/) sponsored by Hired.com.
Like FRESCA, JSON Resume is JSON-driven and open-source. Unlike FRESCA, JSON
Resume targets a worldwide audience where FRESCA is optimized for technical
candidates.
## Should I use the FRESH or JSON Resume format/schema for my resume?
Both! The workflow we like to use:
1. Create a resume in FRESH format for tooling and analysis.
2. Convert it to JSON Resume format for additional themes/tools.
3. Maintain both versions.
Both formats are open-source and both formats are JSON-driven. FRESH was
designed as a universal container format and superset of existing formats, where
the JSON Resume format is intended for a generic audience.
## How do I use a FRESH theme?
Several FRESH themes come preinstalled with HackMyResume; others can be
installed from NPM and GitHub.
### To use a preinstalled FRESH theme:
1. Pass the theme name into HackMyResume via the `--theme` or `-t` parameter:
```bash
hackmyresume build resume.json --theme compact
```
### To use an external FRESH theme:
1. Install the theme locally. The easiest way to do that is with NPM.
```bash
npm install fresh-theme-underscore
```
2. Pass the theme folder into HackMyResume:
```bash
hackmyresume BUILD resume.json --theme node_modules/fresh-theme-underscore
```
3. Check your output folder. It's best to view HTML formats over a local web
server connection.
## How do I use a JSON Resume theme?
JSON Resume (JRS) themes can be installed from NPM and GitHub and passed into
HackMyResume via the `--theme` or `-t` parameter.
1. Install the theme locally. The easiest way to do that is with NPM.
```bash
npm install jsonresume-theme-classy
```
2. Pass the theme folder path into HackMyResume:
```bash
hackmyresume BUILD resume.json --theme node_modules/jsonresume-theme-classy
```
3. Check your output folder. It's best to view HTML formats over a local web
server connection.
## Should I keep my resume in version control?
Absolutely! As text-based, JSON-driven documents, both FRESH and JSON Resume are
ideal candidates for version control. Future versions of HackMyResume will have
this functionality built in.
## Can I change the default section titles ("Employment", "Skills", etc.)?
If you're using a FRESH theme, yes. First, create a HackMyResume options file
mapping resume sections to your preferred section title:
```javascript
// myoptions.json
{
"sectionTitles": {
"employment": "empleo",
"skills": "habilidades",
"education": "educación"
}
}
```
Then, pass the options file into the `-o` or `--opts` parameter:
```bash
hackmyresume BUILD resume.json -o myoptions.json
```
This ability is currently only supported for FRESH resume themes.
## How does resume merging work?
Resume merging is a way of storing your resume in separate files that
HackMyResume will merge into a single "master" resume file prior to generating
specific output formats like HTML or PDF. It's a way of producing flexible,
configurable, targeted resumes with minimal duplication.
For example, a software developer who moonlights as a game programmer might
create three FRESH or JRS resumes at different levels of specificity:
- **generic.json**: A generic technical resume, suitable for all audiences.
- **game-developer.json**: Overrides and amendments for game developer
positions.
- **blizzard.json**: Overrides and amendments specific to a hypothetical
position at Blizzard.
If you run `hackmyresume BUILD generic.json TO out/resume.all`, HMR will
generate all available output formats for the `generic.json` as usual. But if
you instead run...
```bash
hackmyresume BUILD generic.json game-developer.json TO out/resume.all
```
...HackMyResume will notice that multiple source resumes were specified and
merge `game-developer.json` onto `generic.json` before generating, yielding a
resume that's more suitable for game-developer-related positions.
You can take this a step further. Let's say you want to do a targeted resume
submission to a game developer position at Blizzard, and `blizzard.json`
contains the edits and revisions you'd like to show up in the targeted resume.
In that case, merge again! Feed all three resumes to HackMyResume, in order
from most generic to most specific, and HMR will merge them all prior to
generating the final output format(s) for your resume.
```bash
# Merge blizzard.json onto game-developer.json onto generic.json, then build
hackmyresume BUILD generic.json game-developer.json blizzard.json TO out/resume.all
```
There's no limit to the number of resumes you can merge this way.
You can also divide your resume into files containing different sections:
- **resume-a.json**: Contains `info`, `employment`, and `summary` sections.
- **resume-b.json**: Contains all other sections except `references`.
- **references.json**: Contains the private `references` section.
Under that scenario, `hackmyresume BUILD resume-a.json resume-b.json` would
## The HackMyResume terminal color scheme is giving me a headache. Can I disable it?
Yes. Use the `--no-color` option to disable terminal colors:
`hackmyresume <somecommand> <someoptions> --no-color`
## What's the difference between a FRESH theme and a JSON Resume theme?
FRESH themes are multiformat (HTML, Word, PDF, etc.) and required to support
Markdown formatting, configurable section titles, and various other features.
JSON Resume themes are typically HTML-driven, but capable of expansion to other
formats through tools. JSON Resume themes don't support Markdown natively, but
HMR does its best to apply your Markdown, when present, to any JSON Resume
themes it encounters.
## Do I have to have a FRESH resume to use a FRESH theme or a JSON Resume to use a JSON Resume theme?
No. You can mix and match FRESH and JRS-format themes freely. HackMyResume will
perform the necessary conversions on the fly.
## Can I build my own custom FRESH theme?
Yes. The easiest way is to copy an existing FRESH theme, like `modern` or
`compact`, and make your changes there. You can test your theme with:
```bash
hackmyresume build resume.json --theme path/to/my/theme/folder
```
## Can I build my own custom JSON Resume theme?
Yes. The easiest way is to copy an existing JSON Rsume theme and make your
changes there. You can test your theme with:
```bash
hackmyresume build resume.json --theme path/to/my/theme/folder
```
## Can I build my own tools / services / apps / websites around FRESH / FRESCA?
Yes! FRESH/FRESCA formats are 100% open source, permissively licensed under MIT,
and 100% free from company-specific, tool-specific, or commercially oriented
lock-in or cruft. These are clean formats designed for users and builders.
## Can I build my own tools / services / apps / websites around JSON Resume?
Yes! HackMyResume is not affiliated with JSON Resume, but like FRESH/FRESCA,
JSON Resume is open-source, permissively licensed, and free of proprietary
lock-in. See the JSON Resume website for details.

View File

@ -1,7 +1,7 @@
'use strict';
module.exports = function (grunt) {
'use strict';
var opts = {
pkg: grunt.file.readJSON('package.json'),
@ -14,16 +14,57 @@ module.exports = function (grunt) {
ui: 'bdd',
reporter: 'spec'
},
all: { src: ['tests/*.js'] }
all: { src: ['test/*.js'] }
},
jsdoc : {
dist : {
src: ['src/**/*.js'],
options: {
private: true,
destination: 'doc'
}
}
},
clean: ['test/sandbox'],
yuidoc: {
compile: {
name: '<%= pkg.name %>',
description: '<%= pkg.description %>',
version: '<%= pkg.version %>',
url: '<%= pkg.homepage %>',
options: {
paths: 'src/',
//themedir: 'path/to/custom/theme/',
outdir: 'docs/'
}
}
},
jshint: {
options: {
laxcomma: true,
expr: true
},
all: ['Gruntfile.js', 'src/**/*.js', 'test/*.js']
}
};
grunt.initConfig( opts );
grunt.loadNpmTasks('grunt-simple-mocha');
grunt.registerTask('test', 'Test the FluentLib library.', function( config ) {
grunt.task.run( ['simplemocha:all'] );
});
grunt.registerTask('default', [ 'test' ]);
grunt.loadNpmTasks('grunt-contrib-yuidoc');
grunt.loadNpmTasks('grunt-jsdoc');
grunt.loadNpmTasks('grunt-contrib-jshint');
grunt.loadNpmTasks('grunt-contrib-clean');
grunt.registerTask('test', 'Test the HackMyResume library.',
function( config ) { grunt.task.run(['clean','jshint','simplemocha:all']); });
grunt.registerTask('document', 'Generate HackMyResume library documentation.',
function( config ) { grunt.task.run( ['jsdoc'] ); });
grunt.registerTask('default', [ 'test', 'jsdoc' ]);
};

View File

@ -1,7 +1,7 @@
The MIT License
===============
Copyright (c) 2015 James M. Devlin (https://github.com/devlinjd)
Copyright (c) 2016 hacksalot (https://github.com/hacksalot)
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal

512
README.md
View File

@ -1,64 +1,216 @@
fluentCV
========
*Generate beautiful, targeted resumes from your command line or shell.*
HackMyResume
============
FluentCV is a **hackable, data-driven, dev-friendly resume authoring tool** with support for HTML, Markdown, Word, PDF, plain text, smoke signal, carrier pigeon, and other arbitrary-format resumes and CVs.
[![Latest release][img-release]][latest-release]
[![Build status (MASTER)][img-master]][travis-url-master]
[![Build status (DEV)][img-dev]][travis-url-dev]
![](assets/fluentcv_cli_ubuntu.png)
*Create polished résumés and CVs in multiple formats from your command line or
shell. Author in clean Markdown and JSON, export to Word, HTML, PDF, LaTeX,
plain text, and other arbitrary formats. Fight the power, save trees. Compatible
with [FRESH][fresca] and [JRS][6] resumes.*
Looking for a desktop GUI version with pretty timelines and graphs? Check out [FluentCV Desktop][7].
![](assets/hackmyresume.cli.1.6.0.png)
HackMyResume is a dev-friendly, local-only Swiss Army knife for resumes and CVs.
Use it to:
1. **Generate** HTML, Markdown, LaTeX, MS Word, PDF, plain text, JSON, XML,
YAML, print, smoke signal, carrier pigeon, and other arbitrary-format resumes
and CVs, from a single source of truth&mdash;without violating DRY.
2. **Analyze** your resume for keyword density, gaps/overlaps, and other
metrics.
3. **Convert** resumes between [FRESH][fresca] and [JSON Resume][6] formats.
4. **Validate** resumes against either format.
HackMyResume is built with Node.js and runs on recent versions of OS X, Linux,
or Windows. View the [FAQ](FAQ.md).
## Features
- Runs on OS X, Linux, and Windows.
- Store your resume data as a durable, versionable JSON, YML, or XML document.
- Generate multiple targeted resumes in multiple formats, based on your needs.
- Output to HTML, PDF, Markdown, Word, JSON, YAML, XML, or a custom format.
- Never update one piece of information in four different resumes again.
- Compatible with the [JSON Resume standard][6] and [authoring tools][7].
- OS X, Linux, and Windows.
- Choose from dozens of FRESH or JSON Resume themes.
- Private, local-only resume authoring and analysis.
- Analyze your resume for keywords, gaps, and other metrics.
- Store your resume data as a durable, versionable JSON or YAML document.
- Generate polished resumes in multiple formats without violating [DRY][dry].
- Output to HTML, Markdown, LaTeX, PDF, MS Word, JSON, YAML, plain text, or XML.
- Validate resumes against the FRESH or JSON Resume schema.
- Support for multiple input and output resumes.
- Convert between FRESH and JSON Resume resumes.
- Use from your command line or [desktop][7].
- Free and open-source through the MIT license.
- Forthcoming: StackOverflow and LinkedIn support.
- Forthcoming: More themes!
- Updated daily / weekly. Contributions are [welcome](CONTRIBUTING.md).
## Install
FluentCV requires a recent version of [Node.js][4] and [NPM][5]. Then:
Install the latest stable version of HackMyResume with NPM:
1. (Optional, for PDF support) Install the latest official [wkhtmltopdf][3] binary for your platform.
2. Install **fluentCV** by running `npm install fluentcv -g`.
3. You're ready to go.
```bash
[sudo] npm install hackmyresume -g
```
Alternately, install the latest bleeding-edge version (updated daily):
```bash
[sudo] npm install hacksalot/hackmyresume#dev -g
```
## Installing PDF Support (optional)
HackMyResume tries not to impose a specific PDF engine requirement on
the user, but will instead work with whatever PDF engines you have installed.
Currently, HackMyResume's PDF generation requires either [Phantom.js][2] or
[wkhtmltopdf][3] to be installed on your system and the `phantomjs` and/or
`wkhtmltopdf` binaries to be accessible on your PATH. This is an optional
requirement for users who care about PDF formats. If you don't care about PDF
formats, skip this step.
## Installing Themes
HackMyResume supports both [FRESH][fresh-themes] and [JSON Resume][jrst]-style
résumé themes.
- FRESH themes currently come preinstalled with HackMyResume.
- JSON Resume themes can be installed from NPM, GitHub, or manually.
To install a JSON Resume theme, just `cd` to the folder where you want to store
your themes and run one of:
```bash
# Install with NPM
npm install jsonresume-theme-[theme-name]
# Install with GitHub
git clone https://github.com/[user-or-org]/[repo-name]
```
Then when you're ready to generate your resume, just reference the location of
the theme folder as you installed it:
```bash
hackmyresume BUILD resume.json TO out/resume.all -t node_modules/jsonresume-theme-classy
```
Note: You can use install themes anywhere on your file system. You don't need a
package.json or other NPM/Node infrastructure.
## Getting Started
To use HackMyResume you'll need to create a valid resume in either
[FRESH][fresca] or [JSON Resume][6] format. Then you can start using the command
line tool. There are five basic commands you should be aware of:
- **build** generates resumes in HTML, Word, Markdown, PDF, and other formats.
Use it when you need to submit, upload, print, or email resumes in specific
formats.
```bash
# hackmyresume BUILD <INPUTS...> TO <OUTPUTS...> [-t THEME]
hackmyresume BUILD resume.json TO out/resume.all
hackmyresume BUILD r1.json r2.json TO out/rez.html out/rez.md foo/rez.all
```
- **new** creates a new resume in FRESH or JSON Resume format.
```bash
# hackmyresume NEW <OUTPUTS...> [-f <FORMAT>]
hackmyresume NEW resume.json
hackmyresume NEW resume.json -f fresh
hackmyresume NEW r1.json r2.json -f jrs
```
- **analyze** inspects your resume for keywords, duration, and other metrics.
```bash
# hackmyresume ANALYZE <INPUTS...>
hackmyresume ANALYZE resume.json
hackmyresume ANALYZE r1.json r2.json
```
- **convert** converts your source resume between FRESH and JSON Resume
formats. Use it to convert between the two formats to take advantage of tools
and services.
```bash
# hackmyresume CONVERT <INPUTS...> TO <OUTPUTS...>
hackmyresume CONVERT resume.json TO resume-jrs.json
hackmyresume CONVERT 1.json 2.json 3.json TO out/1.json out/2.json out/3.json
```
- **validate** validates the specified resume against either the FRESH or JSON
Resume schema. Use it to make sure your resume data is sufficient and complete.
```bash
# hackmyresume VALIDATE <INPUTS...>
hackmyresume VALIDATE resume.json
hackmyresume VALIDATE r1.json r2.json r3.json
```
- **peek** echoes your resume or any field, property, or object path on your
resume to standard output.
```bash
# hackmyresume PEEK <INPUTS...> [OBJECT-PATH]
hackmyresume PEEK rez.json # Echo the whole resume
hackmyresume PEEK rez.json info.brief # Echo the "info.brief" field
hackmyresume PEEK rez.json employment.history[1] # Echo the 1st job
hackmyresume PEEK rez.json rez2.json info.brief # Compare value
```
## Supported Output Formats
HackMyResume supports these output formats:
Output Format | Ext | Notes
------------- | --- | -----
HTML | .html | A standard HTML 5 + CSS resume format that can be viewed in a browser, deployed to a website, etc.
Markdown | .md | A structured Markdown document that can be used as-is or used to generate HTML.
LaTeX | .tex | A structured LaTeX document (or collection of documents) that can be processed with pdflatex, xelatex, and similar tools.
MS Word | .doc | A Microsoft Word office document (XML-driven; WordProcessingML).
Adobe Acrobat (PDF) | .pdf | A binary PDF document driven by an HTML theme (through wkhtmltopdf).
plain text | .txt | A formatted plain text document appropriate for emails or copy-paste.
JSON | .json | A JSON representation of the resume.
YAML | .yml | A YAML representation of the resume.
RTF | .rtf | Forthcoming.
Textile | .textile | Forthcoming.
image | .png, .bmp | Forthcoming.
## Use
Assuming you've got a JSON-formatted resume handy, generating resumes in different formats and combinations easy. Just run:
Assuming you've got a JSON-formatted resume handy, generating resumes in
different formats and combinations is easy. Just run:
```bash
fluentcv [inputs] [outputs] [-t theme].
hackmyresume BUILD <INPUTS> <OUTPUTS> [-t theme].
```
Where `[inputs]` is one or more .json resume files, separated by spaces; `[outputs]` is one or more destination resumes, each prefaced with the `-o` option; and `[theme]` is the desired theme. For example:
Where `<INPUTS>` is one or more .json resume files, separated by spaces;
`<OUTPUTS>` is one or more destination resumes, and `<THEME>` is the desired
theme (default to Modern). For example:
```bash
# Generate all resume formats (HTML, PDF, DOC, TXT, YML, etc.)
fluentcv resume.json -o out/resume.all -t modern
hackmyresume BUILD resume.json TO out/resume.all -t modern
# Generate a specific resume format
fluentcv resume.json -o out/resume.html
fluentcv resume.json -o out/resume.pdf
fluentcv resume.json -o out/resume.md
fluentcv resume.json -o out/resume.doc
fluentcv resume.json -o out/resume.json
fluentcv resume.json -o out/resume.txt
fluentcv resume.json -o out/resume.yml
hackmyresume BUILD resume.json TO out/resume.html
hackmyresume BUILD resume.json TO out/resume.pdf
hackmyresume BUILD resume.json TO out/resume.md
hackmyresume BUILD resume.json TO out/resume.doc
hackmyresume BUILD resume.json TO out/resume.json
hackmyresume BUILD resume.json TO out/resume.txt
hackmyresume BUILD resume.json TO out/resume.yml
# Specify 2 inputs and 3 outputs
fluentcv in1.json in2.json -o out.html -o out.doc -o out.pdf
hackmyresume BUILD in1.json in2.json TO out.html out.doc out.pdf
```
You should see something to the effect of:
```
*** FluentCV v0.7.0 ***
*** HackMyResume v1.4.0 ***
Reading JSON resume: foo/resume.json
Applying MODERN Theme (7 formats)
Generating HTML resume: out/resume.html
@ -74,28 +226,56 @@ Generating YAML resume: out/resume.yml
### Applying a theme
You can specify a predefined or custom theme via the optional `-t` parameter. For a predefined theme, include the theme name. For a custom theme, include the path to the custom theme's folder.
HackMyResume can work with any FRESH or JSON Resume theme (the latter must be
installed first). To specify a theme when generating your resume, use the `-t`
or `--theme` parameter:
```bash
fluentcv resume.json -t modern
fluentcv resume.json -t ~/foo/bar/my-custom-theme/
hackmyresume BUILD resume.json TO out/rez.all -t [theme]
```
As of v0.7.0, available predefined themes are `modern`, `minimist`, and `hello-world`.
The `[theme]` parameter can be the name of a predefined theme OR the path to any
FRESH or JSON Resume theme folder:
```bash
hackmyresume BUILD resume.json TO out/rez.all -t modern
hackmyresume BUILD resume.json TO OUT.rez.all -t ../some-folder/my-custom-theme/
hackmyresume BUILD resume.json TO OUT.rez.all -t node_modules/jsonresume-theme-classy
```
FRESH themes are currently pre-installed with HackMyResume. JSON Resume themes
can be installed prior to use:
```bash
# Install a JSON Resume theme into a local node_modules subfolder:
npm install jsonresume-theme-[name]
# Use it with HackMyResume
hackmyresume build resume.json -t node_modules/jsonresume-theme-[name]
```
As of v1.6.0, available predefined FRESH themes are `positive`, `modern`,
`compact`, `minimist`, and `hello-world`. For a list of JSON Resume themes,
check the [NPM Registry](https://www.npmjs.com/search?q=jsonresume-theme).
### Merging resumes
You can **merge multiple resumes together** by specifying them in order from most generic to most specific:
You can **merge multiple resumes together** by specifying them in order from
most generic to most specific:
```bash
# Merge specific.json onto base.json and generate all formats
fluentcv base.json specific.json -o resume.all
hackmyresume BUILD base.json specific.json TO resume.all
```
This can be useful for overriding a base (generic) resume with information from a specific (targeted) resume. For example, you might override your generic catch-all "software developer" resume with specific details from your targeted "game developer" resume, or combine two partial resumes into a "complete" resume. Merging follows conventional [extend()][9]-style behavior and there's no arbitrary limit to how many resumes you can merge:
This can be useful for overriding a base (generic) resume with information from
a specific (targeted) resume. For example, you might override your generic
catch-all "software developer" resume with specific details from your targeted
"game developer" resume, or combine two partial resumes into a "complete"
resume. Merging follows conventional [extend()][9]-style behavior and there's
no arbitrary limit to how many resumes you can merge:
```bash
fluentcv in1.json in2.json in3.json in4.json -o out.html -o out.doc
hackmyresume BUILD in1.json in2.json in3.json in4.json TO out.html out.doc
Reading JSON resume: in1.json
Reading JSON resume: in2.json
Reading JSON resume: in3.json
@ -107,37 +287,231 @@ Generating WORD resume: out.doc
### Multiple targets
You can specify **multiple output targets** and FluentCV will build them:
You can specify **multiple output targets** and HackMyResume will build them:
```bash
# Generate out1.doc, out1.pdf, and foo.txt from me.json.
fluentcv me.json -o out1.doc -o out1.pdf -o foo.txt
```
You can also omit the output file(s) and/or theme completely:
```bash
# Equivalent to "fluentcv resume.json resume.all -t modern"
fluentcv resume.json
hackmyresume BUILD me.json TO out1.doc out1.pdf foo.txt
```
### Using .all
The special `.all` extension tells FluentCV to generate all supported output formats for the given resume. For example, this...
The special `.all` extension tells HackMyResume to generate all supported output
formats for the given resume. For example, this...
```bash
# Generate all resume formats (HTML, PDF, DOC, TXT, etc.)
fluentcv me.json -o out/resume.all
hackmyresume BUILD me.json TO out/resume.all
```
..tells FluentCV to read `me.json` and generate `out/resume.md`, `out/resume.doc`, `out/resume.html`, `out/resume.txt`, `out/resume.pdf`, and `out/resume.json`.
..tells HackMyResume to read `me.json` and generate `out/resume.md`,
`out/resume.doc`, `out/resume.html`, `out/resume.txt`, `out/resume.pdf`, and
`out/resume.json`.
### Building PDFs
*Users who don't care about PDFs can turn off PDF generation across all themes
and formats with the `--pdf none` switch.*
HackMyResume takes a unique approach to PDF generation. Instead of enforcing
a specific PDF engine on users, HackMyResume will attempt to work with whatever
PDF engine you have installed through the engine's command-line interface (CLI).
Currently that means one or both of...
- [wkhtmltopdf][3]
- [Phantom.js][3]
..with support for other engines planned in the future. But for now, **one or
both of these engines must be installed and accessible on your PATH in order to
generate PDF resumes with HackMyResume**. That means you should be able to
invoke either of these tools directly from your shell or terminal without error:
```bash
wkhtmltopdf input.html output.pdf
phantomjs script.js input.html output.pdf
```
Assuming you've installed one or both of these engines on your system, you can
tell HackMyResume which flavor of PDF generation to use via the `--pdf` option
(`-p` for short):
```bash
hackmyresume BUILD resume.json TO out.all --pdf phantom
hackmyresume BUILD resume.json TO out.all --pdf wkhtmltopdf
hackmyresume BUILD resume.json TO out.all --pdf none
```
### Analyzing
HackMyResume can analyze your resume for keywords, employment gaps, and other
metrics. Run:
```bash
hackmyresume ANALYZE <my-resume>.json
```
Depending on the HackMyResume version, you should see output similar to:
```
*** HackMyResume v1.6.0 ***
Reading resume: resume.json
Analyzing FRESH resume: resume.json
SECTIONS (10):
employment: 12
education: 2
service: 1
skills: 8
writing: 1
recognition: 0
social: 4
interests: 2
references: 1
languages: 2
COVERAGE (61.1%):
Total Days: 6034
Employed: 3688
Gaps: 8 [31, 1065, 273, 153, 671, 61, 61, 31]
Overlaps: 1 [243]
KEYWORDS (61):
Node.js: 6 mentions
JavaScript: 9 mentions
SQL Server: 3 mentions
Visual Studio: 6 mentions
Web API: 1 mentions
N-tier / 3-tier: 1 mentions
HTML 5: 1 mentions
JavaScript: 6 mentions
CSS: 2 mentions
Sass / LESS / SCSS: 1 mentions
LAMP: 3 mentions
WISC: 1 mentions
HTTP: 21 mentions
JSON: 1 mentions
XML: 2 mentions
REST: 1 mentions
WebSockets: 2 mentions
Backbone.js: 3 mentions
Angular.js: 1 mentions
Node.js: 4 mentions
NPM: 1 mentions
Bower: 1 mentions
Grunt: 2 mentions
Gulp: 1 mentions
jQuery: 2 mentions
Bootstrap: 3 mentions
Underscore.js: 1 mentions
PhantomJS: 1 mentions
CoffeeScript: 1 mentions
Python: 11 mentions
Perl: 4 mentions
PHP: 7 mentions
MySQL: 12 mentions
PostgreSQL: 4 mentions
NoSQL: 2 mentions
Apache: 2 mentions
AWS: 2 mentions
EC2: 2 mentions
RDS: 3 mentions
S3: 1 mentions
Azure: 1 mentions
Rackspace: 1 mentions
C++: 23 mentions
C++ 11: 1 mentions
Boost: 1 mentions
Xcode: 2 mentions
gcc: 1 mentions
OO&AD: 1 mentions
.NET: 20 mentions
Unity 5: 2 mentions
Mono: 3 mentions
MonoDevelop: 1 mentions
Xamarin: 1 mentions
TOTAL: 180 mentions
```
### Validating
HackMyResume can also validate your resumes against either the [FRESH /
FRESCA][fresca] or [JSON Resume][6] formats. To validate one or more existing
resumes, use the `validate` command:
```bash
# Validate myresume.json against either the FRESH or JSON Resume schema.
hackmyresume VALIDATE resumeA.json resumeB.json
```
HackMyResume will validate each specified resume in turn:
```bash
*** HackMyResume v1.6.0 ***
Validating JSON resume: resumeA.json (INVALID)
Validating JSON resume: resumeB.json (VALID)
```
### Converting
HackMyResume can convert between the [FRESH][fresca] and [JSON Resume][6]
formats. Just run:
```bash
hackmyresume CONVERT <INPUTS> <OUTPUTS>
```
where <INPUTS> is one or more resumes in FRESH or JSON Resume format, and
<OUTPUTS> is a corresponding list of output file names. HackMyResume will
autodetect the format (FRESH or JRS) of each input resume and convert it to the
other format (JRS or FRESH).
### File-based Options
You can pass options into HackMyResume via an external options or ".hackmyrc"
file with the `--options` or `-o` switch:
```bash
hackmyresume BUILD resume.json -o path/to/options.json
```
The options file can contain any documented HackMyResume option, including
`theme`, `silent`, `debug`, `pdf`, `css`, and other settings.
```javascript
{
// Set the default theme to "compact"
"theme": "compact",
// Change the "employment" section title text to "Work"
"sectionTitles": {
"employment": "Work"
}
}
```
If a particular option is specified both on the command line and in an external
options file, the explicit command-line option takes precedence.
```bash
# path/to/options.json specifes the POSITIVE theme
# -t parameter specifies the COMPACT theme
# The -t parameter wins.
hackmyresume BUILD resume.json -o path/to/options.json -t compact
> Reading resume: resume.json
> Applying COMPACT theme (7 formats)
```
### Prettifying
FluentCV applies [js-beautify][10]-style HTML prettification by default to HTML-formatted resumes. To disable prettification, the `--nopretty` or `-n` flag can be used:
HackMyResume applies [js-beautify][10]-style HTML prettification by default to
HTML-formatted resumes. To disable prettification, the `--no-prettify` or `-n`
flag can be used:
```bash
fluentcv resume.json out.all --nopretty
hackmyresume BUILD resume.json out.all --no-prettify
```
### Silent Mode
@ -145,10 +519,26 @@ fluentcv resume.json out.all --nopretty
Use `-s` or `--silent` to run in silent mode:
```bash
fluentcv resume.json -o someFile.all -s
fluentcv resume.json -o someFile.all --silent
hackmyresume BUILD resume.json -o someFile.all -s
hackmyresume BUILD resume.json -o someFile.all --silent
```
### Debug Mode
Use `-d` or `--debug` to force HMR to emit a call stack when errors occur. In
the future, this option will emit detailed error logging.
```bash
hackmyresume BUILD resume.json -d
hackmyresume ANALYZE resume.json --debug
```
## Contributing
HackMyResume is a community-driven free and open source project under the MIT
License. Contributions are encouraged and we respond to all PRs and issues,
usually within 24 hours. See [CONTRIBUTING.md][contribute] for details.
## License
MIT. Go crazy. See [LICENSE.md][1] for details.
@ -163,3 +553,15 @@ MIT. Go crazy. See [LICENSE.md][1] for details.
[8]: https://youtu.be/N9wsjroVlu8
[9]: https://api.jquery.com/jquery.extend/
[10]: https://github.com/beautify-web/js-beautify
[fresh]: https://github.com/fluentdesk/FRESH
[fresca]: https://github.com/fluentdesk/FRESCA
[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
[img-release]: https://img.shields.io/github/release/hacksalot/HackMyResume.svg?label=version
[img-master]: https://img.shields.io/travis/hacksalot/HackMyResume/master.svg
[img-dev]: https://img.shields.io/travis/hacksalot/HackMyResume/dev.svg?label=dev
[travis-url-master]: https://travis-ci.org/hacksalot/HackMyResume?branch=master
[travis-url-dev]: https://travis-ci.org/hacksalot/HackMyResume?branch=dev
[latest-release]: https://github.com/hacksalot/HackMyResume/releases/latest
[contribute]: CONTRIBUTING.md
[fresh-themes]: https://github.com/fluentdesk/fresh-themes
[jrst]: https://www.npmjs.com/search?q=jsonresume-theme

107
ROADMAP.md Normal file
View File

@ -0,0 +1,107 @@
Development Roadmap
===================
## Short-Term
### FluentCV Desktop: Beta 1
The **FluentCV Desktop 1.0 beta release** will present HackMyResume
functionality in a cross-platform desktop application for OS X, Linux, and
Windows.
### GitHub Integration
HackMyResume will offer GitHub integration for versioned resume storage and
retrieval via the `COMMIT` or `STORE` command(s) starting in 1.7.0 or 1.8.0.
### fresh-themes 1.0.0
The **fresh-themes 1.0** release will bring 100% coverage of the FRESH and JRS
object models&mdash;all resume sections and fields&mdash;along with
documentation, theme developer's guide, new themes, and a freeze to the FRESH
theme structure.
### Better LaTeX support
Including Markdown-to-LaTeX translation and more LaTeX-driven themes / formats.
### StackOverflow and LinkedIn support
Will start appearing in v1.7.0, with incremental improvements in 1.8.0 and
beyond.
### Improved resume sorting and arranging
**Better resume sorting** of items and sections: ascending, descending, by
date or other criteria ([#67][i67]).
### Remote resume / theme loading
Support remote loading of themes and resumes over `http`, `https`, and
`git://`. Enable these usage patterns:
```bash
hackmyresume build https://somesite.com/my-resume.json -t informatic
hackmyresume build resume.json -t npm:fresh-theme-ergonomic
hackmyresume analyze https://github.com/foo/my-resume
```
### 100% code coverage
Should reduce certain classes of errors and allow HMR to display a nifty 100%
code coverage badge.
### Improved **documentation and samples**
Expanded documentation and samples throughout.
## Mid-Term
### Cover letters and job descriptions
Add support for schema-driven **cover letters** and **job descriptions**.
### Character Sheets
HackMyResume 2.0 will ship with support for, yes, RPG-style character sheets.
This will demonstrate the tool's ability to flow arbitrary JSON to concrete
document(s) and provide unique albeit niche functionality around various games
([#117][i117]).
### Rich text (.rtf) output formats
Basic support for **rich text** `.rtf` output formats.
### Investigate: groff support
Investigate adding [**groff**][groff] support, because that would, indeed, be
[dope][d] ([#37][i37]).
### Investigate: org-mode support
Investigate adding [**org mode**][om] support ([#38][i38]).
### Investigate: Scribus
Investigate adding [**Scribus SLA**][scri] support ([#54][i54]).
### Support JSON Resume 1.0.0
When released.
## Long-Term
- TBD
[groff]: http://www.gnu.org/software/groff/
[om]: http://orgmode.org/
[scri]: https://en.wikipedia.org/wiki/Scribus
[d]: https://github.com/hacksalot/HackMyResume/issues/37#issue-123818674
[i37]: https://github.com/hacksalot/HackMyResume/issues/37
[i38]: https://github.com/hacksalot/HackMyResume/issues/38
[i54]: https://github.com/hacksalot/HackMyResume/issues/54
[i67]: https://github.com/hacksalot/HackMyResume/issues/67
[i107]: https://github.com/hacksalot/HackMyResume/issues/107
[i117]: https://github.com/hacksalot/HackMyResume/issues/117

Binary file not shown.

Before

Width:  |  Height:  |  Size: 73 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
assets/hackmyresume_cli.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 83 KiB

BIN
assets/resume-bouqet.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 170 KiB

View File

@ -1,48 +1,81 @@
{
"name": "fluentcv",
"version": "0.7.0",
"description": "Generate beautiful, targeted resumes from your command line, shell, or in Javascript with Node.js.",
"name": "hackmyresume",
"version": "1.7.0",
"description": "Generate polished résumés and CVs in HTML, Markdown, LaTeX, MS Word, PDF, plain text, JSON, XML, YAML, smoke signal, and carrier pigeon.",
"repository": {
"type": "git",
"url": "https://github.com/fluentdesk/fluentcv.git"
"url": "https://github.com/hacksalot/HackMyResume.git"
},
"scripts": {
"test": "grunt clean && mocha",
"grunt": "grunt"
},
"keywords": [
"resume",
"CV",
"portfolio",
"Markdown"
"employment",
"career",
"Markdown",
"JSON",
"Word",
"PDF",
"YAML",
"HTML",
"LaTeX",
"CLI",
"Handlebars",
"Underscore",
"template"
],
"author": "hacksalot <hacksalot@indevious.com> (https://github.com/hacksalot)",
"contributors": [
"aruberto (https://github.com/aruberto)",
"jjanusch (https://github.com/driftdev)",
"robertmain (https://github.com/robertmain)",
"tomheon (https://github.com/tomheon)",
"zhuangya (https://github.com/zhuangya)",
"hacksalot <hacksalot@indevious.com> (https://github.com/hacksalot)"
],
"author": "James M. Devlin",
"license": "MIT",
"preferGlobal": "true",
"bugs": {
"url": "https://github.com/fluentdesk/fluentcv/issues"
"url": "https://github.com/hacksalot/HackMyResume/issues"
},
"main": "src/fluentcmd.js",
"bin": {
"fluentcv": "src/index.js"
"hackmyresume": "src/index.js"
},
"homepage": "https://github.com/fluentdesk/fluentcv",
"homepage": "https://github.com/hacksalot/HackMyResume",
"dependencies": {
"fluent-themes": "0.1.0-beta",
"fs-extra": "^0.24.0",
"html": "0.0.10",
"is-my-json-valid": "^2.12.2",
"jst": "0.0.13",
"marked": "^0.3.5",
"minimist": "^1.2.0",
"mkdirp": "^0.5.1",
"moment": "^2.10.6",
"chalk": "^1.1.1",
"commander": "^2.9.0",
"copy": "^0.1.3",
"extend": "^3.0.0",
"fresca": "~0.6.0",
"hackmycore": "^0.2.0",
"handlebars": "^4.0.5",
"lodash": "^3.10.1",
"printf": "^0.2.3",
"string-padding": "^1.0.2",
"string.prototype.startswith": "^0.2.0",
"underscore": "^1.8.3",
"wkhtmltopdf": "^0.1.5",
"xml-escape": "^1.0.0",
"word-wrap": "^1.1.0",
"yamljs": "^0.2.4"
},
"devDependencies": {
"chai": "*",
"fresh-test-resumes": "^0.6.0",
"grunt": "*",
"grunt-cli": "^0.1.13",
"grunt-contrib-clean": "^0.7.0",
"grunt-contrib-jshint": "^0.11.3",
"grunt-contrib-yuidoc": "^0.10.0",
"grunt-jsdoc": "^1.1.0",
"grunt-simple-mocha": "*",
"is-my-json-valid": "^2.12.2",
"jsonresume-theme-boilerplate": "^0.1.2",
"jsonresume-theme-classy": "^1.0.9",
"jsonresume-theme-modern": "0.0.18",
"jsonresume-theme-sceptile": "^1.0.5",
"mocha": "*",
"resample": "fluentdesk/resample"
}

30
src/cli/analyze.hbs Normal file
View File

@ -0,0 +1,30 @@
{{style "SECTIONS (" "bold"}}{{style totals.numSections "white" }}{{style ")" "bold"}}
employment: {{v totals.totals.employment "-" 2 "bold" }}
projects: {{v totals.totals.projects "-" 2 "bold" }}
education: {{v totals.totals.education "-" 2 "bold" }}
service: {{v totals.totals.service "-" 2 "bold" }}
skills: {{v totals.totals.skills "-" 2 "bold" }}
writing: {{v totals.totals.writing "-" 2 "bold" }}
speaking: {{v totals.totals.speaking "-" 2 "bold" }}
reading: {{v totals.totals.reading "-" 2 "bold" }}
social: {{v totals.totals.social "-" 2 "bold" }}
references: {{v totals.totals.references "-" 2 "bold" }}
testimonials: {{v totals.totals.testimonials "-" 2 "bold" }}
languages: {{v totals.totals.languages "-" 2 "bold" }}
interests: {{v totals.totals.interests "-" 2 "bold" }}
{{style "COVERAGE (" "bold"}}{{style coverage.pct "white"}}{{style ")" "bold"}}
Total Days: {{v coverage.duration.total "-" 5 "bold" }}
Employed: {{v coverage.duration.work "-" 5 "bold" }}
Gaps: {{v coverage.gaps.length "-" 5 "bold" }} [{{#if coverage.gaps.length }}{{#each coverage.gaps }}{{#unless @first}} {{/unless}}{{gapLength duration }}{{/each}}{{/if}}]
Overlaps: {{v coverage.overlaps.length "-" 5 "bold" }} [{{#if coverage.overlaps.length }}{{#each coverage.overlaps }}{{#unless @first}} {{/unless}}{{gapLength duration }}{{/each}}{{/if}}]
{{style "KEYWORDS (" "bold"}}{{style keywords.length "white" }}{{style ")" "bold"}}
{{#each keywords }}{{{pad name 18}}}: {{v count "-" 5 "bold"}} mention{{#isPlural count}}s{{/isPlural}}
{{/each}}
-------------------------------
{{v keywords.length "0" 9 "bold"}} {{style "KEYWORDS" "bold"}} {{v keywords.totalKeywords "0" 5 "bold"}} {{style "mentions" "bold"}}

280
src/cli/error.js Normal file
View File

@ -0,0 +1,280 @@
/**
Error-handling routines for HackMyResume.
@module cli/error
@license MIT. See LICENSE.md for details.
*/
(function() {
var HMSTATUS = require('hackmycore/src/core/status-codes')
, PKG = require('../../package.json')
, FS = require('fs')
, FCMD = require('hackmycore')
, PATH = require('path')
, WRAP = require('word-wrap')
, M2C = require('hackmycore/src/utils/md2chalk.js')
, chalk = require('chalk')
, extend = require('extend')
, YAML = require('yamljs')
, printf = require('printf')
, SyntaxErrorEx = require('hackmycore/src/utils/syntax-error-ex');
require('string.prototype.startswith');
/**
Error handler for HackMyResume. All errors are handled here.
@class ErrorHandler
*/
var ErrorHandler = module.exports = {
init: function( debug, assert, silent ) {
this.debug = debug;
this.assert = assert;
this.silent = silent;
this.msgs = require('./msg.js').errors;
return this;
},
err: function( ex, shouldExit ) {
// Short-circuit logging output if --silent is on
var o = this.silent ? function() { } : _defaultLog;
// Special case; can probably be removed.
if( ex.pass ) throw ex;
// Load error messages
this.msgs = this.msgs || require('./msg.js').errors;
// Handle packaged HMR exceptions
if( ex.fluenterror ) {
// Output the error message
var objError = assembleError.call( this, ex );
o( this[ 'format_' + objError.etype ]( objError.msg ));
// Output the stack (sometimes)
if( objError.withStack ) {
var stack = ex.stack || (ex.inner && ex.inner.stack);
stack && o( chalk.gray( stack ) );
}
// Quit if necessary
if( ex.quit || objError.quit ) {
this.debug && o(
chalk.cyan('Exiting with error code ' + ex.fluenterror.toString()));
if( this.assert ) { ex.pass = true; throw ex; }
process.exit( ex.fluenterror );
}
}
// Handle raw exceptions
else {
o( ex );
var stackTrace = ex.stack || (ex.inner && ex.inner.stack);
if( stackTrace && this.debug )
o( M2C(ex.stack || ex.inner.stack, 'gray') );
}
},
format_error: function( msg ) {
msg = msg || '';
return chalk.red.bold(
msg.toUpperCase().startsWith('ERROR:') ? msg : 'Error: ' + msg );
},
format_warning: function( brief, msg ) {
return chalk.yellow(brief) + chalk.yellow(msg || '');
},
format_custom: function( msg ) {
return msg;
}
};
function _defaultLog() {
console.log.apply( console.log, arguments );
}
function assembleError( ex ) {
var msg = '', withStack = false, quit = false, etype = 'warning';
if( this.debug ) withStack = true;
switch( ex.fluenterror ) {
case HMSTATUS.themeNotFound:
msg = printf( M2C( this.msgs.themeNotFound.msg, 'yellow' ), ex.data);
break;
case HMSTATUS.copyCSS:
msg = M2C( this.msgs.copyCSS.msg, 'red' );
quit = false;
break;
case HMSTATUS.resumeNotFound:
msg = M2C( this.msgs.resumeNotFound.msg, 'yellow' );
break;
case HMSTATUS.missingCommand:
msg = M2C( this.msgs.missingCommand.msg + " (", 'yellow');
msg += Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
return (idx === ar.length - 1 ? chalk.yellow('or ') : '') +
chalk.yellow.bold(v.toUpperCase());
}).join( chalk.yellow(', ')) + chalk.yellow(").\n\n");
msg += chalk.gray(FS.readFileSync(
PATH.resolve(__dirname, '../cli/use.txt'), 'utf8' ));
break;
case HMSTATUS.invalidCommand:
msg = printf( M2C( this.msgs.invalidCommand.msg, 'yellow'), ex.attempted );
break;
case HMSTATUS.resumeNotFoundAlt:
msg = M2C( this.msgs.resumeNotFoundAlt.msg, 'yellow' );
break;
case HMSTATUS.inputOutputParity:
msg = M2C( this.msgs.inputOutputParity.msg );
break;
case HMSTATUS.createNameMissing:
msg = M2C( this.msgs.createNameMissing.msg );
break;
case HMSTATUS.pdfGeneration:
msg = M2C( this.msgs.pdfGeneration.msg, 'bold' );
if( ex.inner ) msg += chalk.red('\n' + ex.inner);
withStack = true; quit = false; etype = 'error';
break;
case HMSTATUS.invalid:
msg = M2C( this.msgs.invalid.msg, 'red' );
etype = 'error';
break;
case HMSTATUS.generateError:
msg = (ex.inner && ex.inner.toString()) || ex;
quit = false;
etype = 'error';
break;
case HMSTATUS.fileSaveError:
msg = printf( M2C( this.msgs.fileSaveError.msg ), (ex.inner || ex).toString() );
etype = 'error';
quit = false;
break;
case HMSTATUS.invalidFormat:
ex.data.forEach(function(d){
msg += printf( M2C( this.msgs.invalidFormat.msg, 'bold' ),
ex.theme.name.toUpperCase(), d.format.toUpperCase());
}, this);
break;
case HMSTATUS.missingParam:
msg = printf( M2C( this.msgs.missingParam.msg ), ex.expected, ex.helper );
break;
case HMSTATUS.invalidHelperUse:
msg = printf( M2C( this.msgs.invalidHelperUse.msg ), ex.helper );
if( ex.error ) {
msg += '\n--> ' + assembleError.call( this, extend( true, {}, ex, {fluenterror: ex.error} )).msg;
//msg += printf( '\n--> ' + M2C( this.msgs.invalidParamCount.msg ), ex.expected );
}
quit = false;
etype = 'warning';
break;
case HMSTATUS.notOnPath:
msg = printf( M2C(this.msgs.notOnPath.msg, 'bold'), ex.engine);
quit = false;
etype = 'error';
break;
case HMSTATUS.readError:
if( !ex.quiet )
console.error(printf( M2C(this.msgs.readError.msg, 'red'), ex.file));
msg = ex.inner.toString();
etype = 'error';
break;
case HMSTATUS.mixedMerge:
msg = M2C( this.msgs.mixedMerge.msg );
quit = false;
break;
case HMSTATUS.invokeTemplate:
msg = M2C( this.msgs.invokeTemplate.msg, 'red' );
msg += M2C( '\n' + WRAP(ex.inner.toString(), { width: 60, indent: ' ' }), 'gray' );
etype = 'custom';
break;
case HMSTATUS.compileTemplate:
etype = 'error';
break;
case HMSTATUS.themeLoad:
msg = M2C( printf( this.msgs.themeLoad.msg, ex.attempted.toUpperCase() ), 'red');
if( ex.inner && ex.inner.fluenterror ) {
msg += M2C('\nError: ', 'red') + assembleError.call( this, ex.inner ).msg;
}
quit = true;
etype = 'custom';
break;
case HMSTATUS.parseError:
if( SyntaxErrorEx.is( ex.inner )) {
console.error( printf( M2C(this.msgs.readError.msg, 'red'), ex.file ) );
var se = new SyntaxErrorEx( ex, ex.raw );
msg = printf( M2C( this.msgs.parseError.msg, 'red' ),
se.line, se.col);
}
else if( ex.inner && ex.inner.line !== undefined && ex.inner.col !== undefined ) {
msg = printf( M2C( this.msgs.parseError.msg, 'red' ),
ex.inner.line, ex.inner.col);
}
else {
msg = ex;
}
etype = 'error';
break;
}
return {
msg: msg, // The error message to display
withStack: withStack, // Whether to include the stack
quit: quit,
etype: etype
};
}
}());

331
src/cli/main.js Normal file
View File

@ -0,0 +1,331 @@
/**
Definition of the `main` function.
@module cli/main
@license MIT. See LICENSE.md for details.
*/
(function(){
var HMR = require( 'hackmycore')
, PKG = require('../../package.json')
, FS = require('fs')
, EXTEND = require('extend')
, chalk = require('chalk')
, PATH = require('path')
, HMSTATUS = require('hackmycore/src/core/status-codes')
, HME = require('hackmycore/src/core/event-codes')
, safeLoadJSON = require('hackmycore/src/utils/safe-json-loader')
, StringUtils = require('hackmycore/src/utils/string.js')
, _ = require('underscore')
, OUTPUT = require('./out')
, PAD = require('string-padding')
, Command = require('commander').Command;
var _opts = { };
var _title = chalk.white.bold('\n*** HackMyResume v' +PKG.version+ ' ***');
var _out = new OUTPUT( _opts );
/**
A callable implementation of the HackMyResume CLI. Encapsulates the command
line interface as a single method accepting a parameter array.
@alias module:cli/main.main
@param rawArgs {Array} An array of command-line parameters. Will either be
process.argv (in production) or custom parameters (in test).
*/
var main = module.exports = function( rawArgs ) {
var initInfo = initialize( rawArgs );
var args = initInfo.args;
// Create the top-level (application) command...
var program = new Command('hackmyresume')
.version(PKG.version)
.description(chalk.yellow.bold('*** HackMyResume ***'))
.option('-s --silent', 'Run in silent mode')
.option('--no-color', 'Disable colors')
.option('--color', 'Enable colors')
.option('-d --debug', 'Enable diagnostics', false)
.option('-a --assert', 'Treat warnings as errors', false)
.option('-v --version', 'Show the version')
.allowUnknownOption();
program.jsonArgs = initInfo.options;
// Create the NEW command
program
.command('new')
.arguments('<sources...>')
.option('-f --format <fmt>', 'FRESH or JRS format', 'FRESH')
.alias('create')
.description('Create resume(s) in FRESH or JSON RESUME format.')
.action(function( sources ) {
execute.call( this, sources, [], this.opts(), logMsg);
});
// Create the VALIDATE command
program
.command('validate')
.arguments('<sources...>')
.description('Validate a resume in FRESH or JSON RESUME format.')
.action(function(sources) {
execute.call( this, sources, [], this.opts(), logMsg);
});
// Create the CONVERT command
program
.command('convert')
.description('Convert a resume to/from FRESH or JSON RESUME format.')
.action(function() {
var x = splitSrcDest.call( this );
execute.call( this, x.src, x.dst, this.opts(), logMsg);
});
// Create the ANALYZE command
program
.command('analyze')
.arguments('<sources...>')
.description('Analyze one or more resumes.')
.action(function( sources ) {
execute.call( this, sources, [], this.opts(), logMsg);
});
// Create the PEEK command
program
.command('peek')
.arguments('<sources...>')
.description('Peek at a resume field or section')
.action(function( sources, sectionOrField ) {
var dst = (sources && sources.length > 1) ? [sources.pop()] : [];
execute.call( this, sources, dst, this.opts(), logMsg);
});
// Create the BUILD command
program
.command('build')
.alias('generate')
.option('-t --theme <theme>', 'Theme name or path')
.option('-n --no-prettify', 'Disable HTML prettification', true)
.option('-c --css <option>', 'CSS linking / embedding')
.option('-p --pdf <engine>', 'PDF generation engine')
.option('--no-sort', 'Sort resume sections by date', false)
.option('--tips', 'Display theme tips and warnings.', false)
.description('Generate resume to multiple formats')
.action(function( sources, targets, options ) {
var x = splitSrcDest.call( this );
execute.call( this, x.src, x.dst, this.opts(), logMsg);
});
program.parse( args );
if (!program.args.length) { throw { fluenterror: 4 }; }
};
/** Massage command-line args and setup Commander.js. */
function initialize( ar ) {
var o = initOptions( ar );
o.silent || logMsg( _title );
// Emit debug prelude if --debug was specified
if( o.debug ) {
_out.log(chalk.cyan('The -d or --debug switch was specified. DEBUG mode engaged.'));
_out.log('');
_out.log(chalk.cyan(PAD(' Platform:',25, null, PAD.RIGHT)) + chalk.cyan.bold( process.platform === 'win32' ? 'windows' : process.platform ));
_out.log(chalk.cyan(PAD(' Node.js:',25, null, PAD.RIGHT)) + chalk.cyan.bold( process.version ));
_out.log(chalk.cyan(PAD(' HackMyResume:',25, null, PAD.RIGHT)) + chalk.cyan.bold('v' + PKG.version ));
_out.log(chalk.cyan(PAD(' FRESCA:',25, null, PAD.RIGHT)) + chalk.cyan.bold( PKG.dependencies.fresca ));
//_out.log(chalk.cyan(PAD(' fresh-themes:',25, null, PAD.RIGHT)) + chalk.cyan.bold( PKG.dependencies['fresh-themes'] ));
//_out.log(chalk.cyan(PAD(' fresh-jrs-converter:',25, null, PAD.RIGHT)) + chalk.cyan.bold( PKG.dependencies['fresh-jrs-converter'] ));
_out.log('');
}
// Handle invalid verbs here (a bit easier here than in commander.js)...
if( o.verb && !HMR.verbs[ o.verb ] && !HMR.alias[ o.verb ] ) {
throw { fluenterror: HMSTATUS.invalidCommand, quit: true,
attempted: o.orgVerb };
}
// Override the .missingArgument behavior
Command.prototype.missingArgument = function(name) {
if( this.name() !== 'new' ) {
throw { fluenterror: HMSTATUS.resumeNotFound, quit: true };
}
};
// Override the .helpInformation behavior
Command.prototype.helpInformation = function() {
var manPage = FS.readFileSync(
PATH.join(__dirname, 'use.txt'), 'utf8' );
return chalk.green.bold(manPage);
};
return {
args: o.args,
options: o.json
};
}
/** Init options prior to setting up command infrastructure. */
function initOptions( ar ) {
var oVerb, verb = '', args = ar.slice(), cleanArgs = args.slice(2), oJSON;
if( cleanArgs.length ) {
// Support case-insensitive sub-commands (build, generate, validate, etc)
var vidx = _.findIndex( cleanArgs, function(v){ return v[0] !== '-'; });
if( vidx !== -1 ) {
oVerb = cleanArgs[ vidx ];
verb = args[ vidx + 2 ] = oVerb.trim().toLowerCase();
}
// Remove --options --opts -o and process separately
var optsIdx = _.findIndex( cleanArgs, function(v){
return v === '-o' || v === '--options' || v === '--opts';
});
if(optsIdx !== -1) {
var optStr = cleanArgs[ optsIdx + 1];
args.splice( optsIdx + 2, 2 );
if( optStr && (optStr = optStr.trim()) ) {
//var myJSON = JSON.parse(optStr);
if( optStr[0] === '{')
oJSON = eval('(' + optStr + ')'); // jshint ignore:line
else {
var inf = safeLoadJSON( optStr );
if( !inf.ex )
oJSON = inf.json;
// TODO: Error handling
}
}
}
}
// Grab the --debug flag
var isDebug = _.some( args, function(v) {
return v === '-d' || v === '--debug';
});
// Grab the --silent flag
var isSilent = _.some( args, function(v) {
return v === '-s' || v === '--silent';
});
return {
debug: isDebug,
silent: isSilent,
orgVerb: oVerb,
verb: verb,
json: oJSON,
args: args
};
}
/** Invoke a HackMyResume verb. */
function execute( src, dst, opts, log ) {
loadOptions.call( this, opts, this.parent.jsonArgs );
var hand = require( './error' );
hand.init( _opts.debug, _opts.assert, _opts.silent );
var v = new HMR.verbs[ this.name() ]();
_opts.errHandler = v;
_out.init( _opts );
v.on( 'hmr:status', function() { _out.do.apply( _out, arguments ); });
v.on( 'hmr:error', function() {
hand.err.apply( hand, arguments );
});
v.invoke.call( v, src, dst, _opts, log );
if( v.errorCode )
process.exit(v.errorCode);
}
/**
Initialize HackMyResume options.
TODO: Options loading is a little hacky, for two reasons:
- Commander.js idiosyncracies
- Need to accept JSON inputs from the command line.
*/
function loadOptions( o, cmdO ) {
// o and this.opts() seem to be the same (command-specific options)
// Load the specified options file (if any) and apply options
if( cmdO )
o = EXTEND(true, o, cmdO);
// Merge in command-line options
o = EXTEND( true, o, this.opts() );
// Kludge parent-level options until piping issue is resolved
if( this.parent.silent !== undefined && this.parent.silent !== null)
o.silent = this.parent.silent;
if( this.parent.debug !== undefined && this.parent.debug !== null)
o.debug = this.parent.debug;
if( this.parent.assert !== undefined && this.parent.assert !== null)
o.assert = this.parent.assert;
if( o.debug ) {
logMsg(chalk.cyan('OPTIONS:') + '\n');
_.each(o, function(val, key) {
logMsg(chalk.cyan(' %s') + chalk.cyan.bold(' %s'),
PAD(key,22,null,PAD.RIGHT), val);
});
logMsg('');
}
// Cache
EXTEND(true, _opts, o);
}
/** Split multiple command-line filenames by the 'TO' keyword */
function splitSrcDest() {
var params = this.parent.args.filter(function(j) { return String.is(j); });
if( params.length === 0 )
throw { fluenterror: HMSTATUS.resumeNotFound, quit: true };
// Find the TO keyword, if any
var splitAt = _.findIndex( params, function(p) {
return p.toLowerCase() === 'to';
});
// TO can't be the last keyword
if( splitAt === params.length - 1 && splitAt !== -1 ) {
logMsg(chalk.yellow('Please ') +
chalk.yellow.bold('specify an output file') +
chalk.yellow(' for this operation or ') +
chalk.yellow.bold('omit the TO keyword') +
chalk.yellow('.') );
return;
}
return {
src: params.slice(0, splitAt === -1 ? undefined : splitAt ),
dst: splitAt === -1 ? [] : params.slice( splitAt + 1 )
};
}
/** Simple logging placeholder. */
function logMsg() {
_opts.silent || console.log.apply( console.log, arguments );
}
}());

18
src/cli/msg.js Normal file
View File

@ -0,0 +1,18 @@
/**
Message-handling routines for HackMyResume.
@module msg.js
@license MIT. See LICENSE.md for details.
*/
(function() {
var PATH = require('path');
var YAML = require('yamljs');
var cache = module.exports = function() {
return cache ? cache : YAML.load( PATH.join(__dirname, 'msg.yml') );
}();
}());

98
src/cli/msg.yml Normal file
View File

@ -0,0 +1,98 @@
events:
begin:
msg: Invoking **%s** command.
beforeCreate:
msg: Creating new **%s** resume: **%s**
afterRead:
msg: Reading **%s** resume: **%s**
beforeTheme:
msg: Verifying **%s** theme.
afterTheme:
msg: Verifying outputs: ???
beforeMerge:
msg:
- "Merging **%s**"
- " onto **%s**"
applyTheme:
msg: Applying **%s** theme (**%s** format%s)
afterBuild:
msg:
- "The **%s** theme says:"
- |
"For best results view JSON Resume themes over a
local or remote HTTP connection. For example:
npm install http-server -g
http-server <resume-folder>
For more information, see the README."
afterGenerate:
msg:
- " (with %s)"
- "Skipping %s resume: %s"
- "Generating **%s** resume: **%s**"
beforeAnalyze:
msg: "Analyzing **%s** resume: **%s**"
beforeConvert:
msg: "Converting **%s** (**%s**) to **%s** (**%s**)"
afterValidate:
msg:
- "Validating **%s** against the **%s** schema: "
- "VALID!"
- "INVALID"
- "BROKEN"
beforePeek:
msg:
- Peeking at **%s** in **%s**
- Peeking at **%s**
afterPeek:
msg: "The specified key **%s** was not found in **%s**."
afterInlineConvert:
msg: Converting **%s** to **%s** format.
errors:
themeNotFound:
msg: >
**Couldn't find the '%s' theme.** Please specify the name of a preinstalled
FRESH theme or the path to a locally installed FRESH or JSON Resume theme.
copyCSS:
msg: Couldn't copy CSS file to destination folder.
resumeNotFound:
msg: Please **feed me a resume** in FRESH or JSON Resume format.
missingCommand:
msg: Please **give me a command**
invalidCommand:
msg: Invalid command: '%s'
resumeNotFoundAlt:
msg: Please **feed me a resume** in either FRESH or JSON Resume format.
inputOutputParity:
msg: Please **specify an output file name** for every input file you wish to convert.
createNameMissing:
msg: Please **specify the filename** of the resume to create.
pdfGeneration:
msg: PDF generation failed. Make sure wkhtmltopdf is installed and accessible from your path.
invalid:
msg: Validation failed and the --assert option was specified.
invalidFormat:
msg: The **%s** theme doesn't support the **%s** format.
notOnPath:
msg: %s wasn't found on your system path or is inaccessible. PDF not generated.
readError:
msg: Reading **???** resume: **%s**
parseError:
msg: Invalid or corrupt JSON on line %s column %s.
invalidHelperUse:
msg: "**Warning**: Incorrect use of the **%s** theme helper."
fileSaveError:
msg: An error occurred while writing %s to disk: %s.
mixedMerge:
msg: "**Warning:** merging mixed resume types. Errors may occur."
invokeTemplate:
msg: "An error occurred during template invocation."
compileTemplate:
msg: "An error occurred during template compilation."
themeLoad:
msg: "Applying **%s** theme (? formats)"
invalidParamCount:
msg: "Invalid number of parameters. Expected: **%s**."
missingParam:
msg: The '**%s**' parameter was needed but not supplied.

229
src/cli/out.js Normal file
View File

@ -0,0 +1,229 @@
/**
Output routines for HackMyResume.
@license MIT. See LICENSE.md for details.
@module out.js
*/
(function() {
var chalk = require('chalk')
, HME = require('hackmycore/src/core/event-codes')
, _ = require('underscore')
, Class = require('hackmycore/src/utils/class.js')
, M2C = require('hackmycore/src/utils/md2chalk.js')
, PATH = require('path')
, LO = require('lodash')
, FS = require('fs')
, EXTEND = require('extend')
, HANDLEBARS = require('handlebars')
, YAML = require('yamljs')
, printf = require('printf')
, pad = require('string-padding')
, dbgStyle = 'cyan';
/**
A stateful output module. All HMR console output handled here.
*/
var OutputHandler = module.exports = Class.extend({
init: function( opts ) {
this.opts = EXTEND( true, this.opts || { }, opts );
this.msgs = YAML.load(PATH.join( __dirname, 'msg.yml' )).events;
},
log: function( msg ) {
msg = msg || '';
var printf = require('printf');
var finished = printf.apply( printf, arguments );
this.opts.silent || console.log( finished );
},
do: function( evt ) {
var that = this;
function L() {
that.log.apply( that, arguments );
}
switch( evt.sub ) {
case HME.begin:
this.opts.debug &&
L( M2C( this.msgs.begin.msg, dbgStyle), evt.cmd.toUpperCase() );
break;
case HME.error:
break;
case HME.beforeCreate:
L( M2C( this.msgs.beforeCreate.msg, 'green' ), evt.fmt, evt.file );
break;
case HME.beforeRead:
break;
case HME.afterRead:
break;
case HME.beforeTheme:
this.opts.debug &&
L( M2C( this.msgs.beforeTheme.msg, dbgStyle), evt.theme.toUpperCase() );
break;
case HME.afterParse:
L(
M2C( this.msgs.afterRead.msg, 'gray', 'white.dim'), evt.fmt.toUpperCase(), evt.file
);
break;
case HME.afterTheme:
break;
case HME.beforeMerge:
var msg = '';
evt.f.reverse().forEach( function( a, idx ) {
msg += printf(
((idx === 0) ?
this.msgs.beforeMerge.msg[0] :
this.msgs.beforeMerge.msg[1] ), a.file
);
}, this);
L( M2C(msg, evt.mixed ? 'yellow' : 'gray', 'white.dim') );
break;
case HME.afterMerge:
break;
case HME.applyTheme:
this.theme = evt.theme;
var numFormats = Object.keys( evt.theme.formats ).length;
L( M2C(this.msgs.applyTheme.msg, evt.status === 'error' ? 'red' : 'gray', evt.status === 'error' ? 'bold' : 'white.dim'),
evt.theme.name.toUpperCase(),
numFormats, ( numFormats === 1 ? '' : 's') );
break;
case HME.end:
if( evt.cmd === 'build' ) {
var themeName = this.theme.name.toUpperCase();
if( this.opts.tips && (this.theme.message || this.theme.render) ) {
var WRAP = require('word-wrap');
if( this.theme.message ) {
L( M2C( this.msgs.afterBuild.msg[0], 'cyan' ), themeName );
L( M2C( this.theme.message, 'white' ));
}
else if ( this.theme.render ) {
L( M2C( this.msgs.afterBuild.msg[0], 'cyan'), themeName);
L( M2C( this.msgs.afterBuild.msg[1], 'white'));
}
}
}
break;
case HME.afterGenerate:
var suffix = '';
if( evt.fmt === 'pdf' ) {
if( this.opts.pdf ) {
if( this.opts.pdf !== 'none' ) {
suffix = printf( M2C( this.msgs.afterGenerate.msg[0], evt.error ? 'red' : 'green' ), this.opts.pdf );
}
else {
L( M2C( this.msgs.afterGenerate.msg[1], 'gray' ),
evt.fmt.toUpperCase(), evt.file );
return;
}
}
}
L( M2C( this.msgs.afterGenerate.msg[2] + suffix, evt.error ? 'red' : 'green' ),
pad(evt.fmt.toUpperCase(),4,null,pad.RIGHT),
PATH.relative(process.cwd(), evt.file ));
break;
case HME.beforeAnalyze:
L( M2C( this.msgs.beforeAnalyze.msg, 'green' ), evt.fmt, evt.file);
break;
case HME.afterAnalyze:
var info = evt.info;
var rawTpl = FS.readFileSync( PATH.join( __dirname, 'analyze.hbs' ), 'utf8');
HANDLEBARS.registerHelper( require('../helpers/console-helpers') );
var template = HANDLEBARS.compile(rawTpl, { strict: false, assumeObjects: false });
var tot = 0;
info.keywords.forEach(function(g) { tot += g.count; });
info.keywords.totalKeywords = tot;
var output = template( info );
this.log( chalk.cyan(output) );
break;
case HME.beforeConvert:
L( M2C( this.msgs.beforeConvert.msg, 'green' ),
evt.srcFile, evt.srcFmt, evt.dstFile, evt.dstFmt
);
break;
case HME.afterInlineConvert:
L( M2C( this.msgs.afterInlineConvert.msg, 'gray', 'white.dim' ),
evt.file, evt.fmt );
break;
case HME.afterValidate:
var style = evt.isValid ? 'green' : 'yellow';
L(
M2C( this.msgs.afterValidate.msg[0], 'white' ) +
chalk[style].bold( evt.isValid ?
this.msgs.afterValidate.msg[1] :
this.msgs.afterValidate.msg[2] ),
evt.file, evt.fmt
);
if( evt.errors ) {
_.each(evt.errors, function(err,idx) {
L( chalk.yellow.bold('--> ') +
chalk.yellow(err.field.replace('data.','resume.').toUpperCase() + ' ' +
err.message) );
}, this);
}
break;
case HME.beforePeek:
// if( evt.target )
// L(M2C(this.msgs.beforePeek.msg[0], evt.isError ? 'red' : 'green'), evt.target, evt.file);
// else
// L(M2C(this.msgs.beforePeek.msg[1], evt.isError ? 'red' : 'green'), evt.file);
break;
case HME.afterPeek:
var sty = evt.error ? 'red' : ( evt.target !== undefined ? 'green' : 'yellow' );
if( evt.requested )
L(M2C(this.msgs.beforePeek.msg[0], sty), evt.requested, evt.file);
else
L(M2C(this.msgs.beforePeek.msg[1], sty), evt.file);
if( evt.target !== undefined )
console.dir( evt.target, { depth: null, colors: true } );
else if( !evt.error )
L(M2C( this.msgs.afterPeek.msg, 'yellow'), evt.requested, evt.file);
break;
}
}
});
}());

51
src/cli/use.txt Normal file
View File

@ -0,0 +1,51 @@
Usage:
hackmyresume <command> <sources> [TO <targets>] [<options>]
Available commands:
BUILD Build your resume to the destination format(s).
ANALYZE Analyze your resume for keywords, gaps, and metrics.
VALIDATE Validate your resume for errors and typos.
CONVERT Convert your resume between FRESH and JSON Resume.
NEW Create a new resume in FRESH or JSON Resume format.
PEEK View a specific field or element on your resume.
Available options:
--theme -t Path to a FRESH or JSON Resume theme.
--pdf -p Specify the PDF engine to use (wkhtmltopdf or phantom).
--options -o Load options from an external JSON file.
--format -f The format (FRESH or JSON Resume) to use.
--debug -d Emit extended debugging info.
--assert -a Treat resume validation warnings as errors.
--no-colors Disable terminal colors.
--tips Display theme messages and tips.
--help -h Display help documentation.
--version -v Display the current version.
Not all options are supported for all commands. For example, the
--theme option is only supported for the BUILD command.
Examples:
hackmyresume BUILD resume.json TO out/resume.all --theme modern
hackmyresume ANALYZE resume.json
hackmyresume NEW my-new-resume.json --format JRS
hackmyresume CONVERT resume-fresh.json TO resume-jrs.json
hackmyresume VALIDATE resume.json
hackmyresume PEEK resume.json employment[2].summary
Tips:
- You can specify multiple sources and/or targets for all commands.
- You can use any FRESH or JSON Resume theme with HackMyResume.
- Specify a file extension of .all to generate your resume to all
available formats supported by the theme. (BUILD command.)
- The --theme parameter can specify either the name of a preinstalled
theme, or the path to a local FRESH or JSON Resume theme.
- Visit https://www.npmjs.com/search?q=jsonresume-theme for a full
listing of all available JSON Resume themes.
- Visit https://github.com/fluentdesk/fresh-themes for a complete
listing of all available FRESH themes.
- Report bugs to https://githut.com/hacksalot/HackMyResume/issues.

View File

@ -1,77 +0,0 @@
{
"basics": {
"name": "",
"label": "",
"picture": "",
"email": "",
"phone": "",
"degree": "",
"website": "",
"summary": "",
"location": {
"address": "",
"postalCode": "",
"city": "",
"countryCode": "",
"region": ""
},
"profiles": [{
"network": "",
"username": "",
"url": ""
}]
},
"work": [{
"company": "",
"position": "",
"website": "",
"startDate": "",
"endDate": "",
"summary": "",
"highlights": [
""
]
}],
"awards": [{
"title": "",
"date": "",
"awarder": "",
"summary": ""
}],
"education": [{
"institution": "",
"area": "",
"studyType": "",
"startDate": "",
"endDate": "",
"gpa": "",
"courses": [ "" ]
}],
"publications": [{
"name": "",
"publisher": "",
"releaseDate": "",
"website": "",
"summary": ""
}],
"volunteer": [{
"organization": "",
"position": "",
"website": "",
"startDate": "",
"endDate": "",
"summary": "",
"highlights": [ "" ]
}],
"skills": [{
"name": "",
"level": "",
"keywords": [""]
}]
}

View File

@ -1,80 +0,0 @@
/**
The FluentCV date representation.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var moment = require('moment');
/**
Create a FluentDate from a string or Moment date object. There are a few date
formats to be aware of here.
1. The words "Present" and "Now", referring to the current date
2. The default "YYYY-MM-DD" format used in JSON Resume ("2015-02-10")
3. Year-and-month only ("2015-04")
4. Year-only "YYYY" ("2015")
5. The friendly FluentCV "mmm YYYY" format ("Mar 2015" or "Dec 2008")
6. Empty dates ("", " ")
7. Any other date format that Moment.js can parse from
Note: Moment can transparently parse all or most of these, without requiring us
to specify a date format...but for maximum parsing safety and to avoid Moment
deprecation warnings, it's recommended to either a) explicitly specify the date
format or b) use an ISO format. For clarity, we handle these cases explicitly.
@class FluentDate
*/
function FluentDate( dt ) {
this.rep = this.fmt( dt );
}
FluentDate/*.prototype*/.fmt = function( dt ) {
if( (typeof dt === 'string' || dt instanceof String) ) {
dt = dt.toLowerCase().trim();
if( /^(present|now)$/.test(dt) ) { // "Present", "Now"
return moment();
}
else if( /^\D+\s+\d{4}$/.test(dt) ) { // "Mar 2015"
var parts = dt.split(' ');
var month = (months[parts[0]] || abbr[parts[0]]);
var dt = parts[1] + '-' + (month < 10 ? '0' + month : month.toString());
return moment( dt, 'YYYY-MM' );
}
else if( /^\d{4}-\d{1,2}$/.test(dt) ) { // "2015-03", "1998-4"
return moment( dt, 'YYYY-MM' );
}
else if( /^\s\d{4}$/.test(dt) ) { // "2015"
return moment( dt, 'YYYY' );
}
else if( /^\s*$/.test(dt) ) { // "", " "
var defTime = {
isNull: true,
isBefore: function( other ) {
return( other && !other.isNull ) ? true : false;
},
isAfter: function( other ) {
return( other && !other.isNull ) ? false : false;
},
unix: function() { return 0; },
format: function() { return ''; },
diff: function() { return 0; }
};
return defTime;
}
else {
var mt = moment( dt );
if(mt.isValid())
return mt;
throw 'Invalid date format encountered.';
}
}
else {
if( dt.isValid && dt.isValid() )
return dt;
throw 'Unknown date object encountered.';
}
};
var months = {}, abbr = {};
moment.months().forEach(function(m,idx){months[m.toLowerCase()]=idx+1;});
moment.monthsShort().forEach(function(m,idx){abbr[m.toLowerCase()]=idx+1;});
abbr.sept = 9;
module.exports = FluentDate;

View File

@ -1,380 +0,0 @@
{
"$schema": "http://json-schema.org/draft-04/schema#",
"title": "Resume Schema",
"type": "object",
"additionalProperties": false,
"properties": {
"basics": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string"
},
"label": {
"type": "string",
"description": "e.g. Web Developer"
},
"picture": {
"type": "string",
"description": "URL (as per RFC 3986) to a picture in JPEG or PNG format"
},
"email": {
"type": "string",
"description": "e.g. thomas@gmail.com",
"format": "email"
},
"phone": {
"type": "string",
"description": "Phone numbers are stored as strings so use any format you like, e.g. 712-117-2923"
},
"website": {
"type": "string",
"description": "URL (as per RFC 3986) to your website, e.g. personal homepage",
"format": "uri"
},
"summary": {
"type": "string",
"description": "Write a short 2-3 sentence biography about yourself"
},
"location": {
"type": "object",
"additionalProperties": true,
"properties": {
"address": {
"type": "string",
"description": "To add multiple address lines, use \n. For example, 1234 Glücklichkeit Straße\nHinterhaus 5. Etage li."
},
"postalCode": {
"type": "string"
},
"city": {
"type": "string"
},
"countryCode": {
"type": "string",
"description": "code as per ISO-3166-1 ALPHA-2, e.g. US, AU, IN"
},
"region": {
"type": "string",
"description": "The general region where you live. Can be a US state, or a province, for instance."
}
}
},
"profiles": {
"type": "array",
"description": "Specify any number of social networks that you participate in",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"network": {
"type": "string",
"description": "e.g. Facebook or Twitter"
},
"username": {
"type": "string",
"description": "e.g. neutralthoughts"
},
"url": {
"type": "string",
"description": "e.g. http://twitter.com/neutralthoughts"
}
}
}
}
}
},
"work": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"company": {
"type": "string",
"description": "e.g. Facebook"
},
"position": {
"type": "string",
"description": "e.g. Software Engineer"
},
"website": {
"type": "string",
"description": "e.g. http://facebook.com",
"format": "uri"
},
"startDate": {
"type": "string",
"description": "resume.json uses the ISO 8601 date standard e.g. 2014-06-29",
"format": "date"
},
"endDate": {
"type": "string",
"description": "e.g. 2012-06-29",
"format": "date"
},
"summary": {
"type": "string",
"description": "Give an overview of your responsibilities at the company"
},
"highlights": {
"type": "array",
"description": "Specify multiple accomplishments",
"additionalItems": false,
"items": {
"type": "string",
"description": "e.g. Increased profits by 20% from 2011-2012 through viral advertising"
}
}
}
}
},
"volunteer": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"organization": {
"type": "string",
"description": "e.g. Facebook"
},
"position": {
"type": "string",
"description": "e.g. Software Engineer"
},
"website": {
"type": "string",
"description": "e.g. http://facebook.com",
"format": "uri"
},
"startDate": {
"type": "string",
"description": "resume.json uses the ISO 8601 date standard e.g. 2014-06-29",
"format": "date"
},
"endDate": {
"type": "string",
"description": "e.g. 2012-06-29",
"format": "date"
},
"summary": {
"type": "string",
"description": "Give an overview of your responsibilities at the company"
},
"highlights": {
"type": "array",
"description": "Specify multiple accomplishments",
"additionalItems": false,
"items": {
"type": "string",
"description": "e.g. Increased profits by 20% from 2011-2012 through viral advertising"
}
}
}
}
},
"education": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"institution": {
"type": "string",
"description": "e.g. Massachusetts Institute of Technology"
},
"area": {
"type": "string",
"description": "e.g. Arts"
},
"studyType": {
"type": "string",
"description": "e.g. Bachelor"
},
"startDate": {
"type": "string",
"description": "e.g. 2014-06-29",
"format": "date"
},
"endDate": {
"type": "string",
"description": "e.g. 2012-06-29",
"format": "date"
},
"gpa": {
"type": "string",
"description": "grade point average, e.g. 3.67/4.0"
},
"courses": {
"type": "array",
"description": "List notable courses/subjects",
"additionalItems": false,
"items": {
"type": "string",
"description": "e.g. H1302 - Introduction to American history"
}
}
}
}
},
"awards": {
"type": "array",
"description": "Specify any awards you have received throughout your professional career",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"title": {
"type": "string",
"description": "e.g. One of the 100 greatest minds of the century"
},
"date": {
"type": "string",
"description": "e.g. 1989-06-12",
"format": "date"
},
"awarder": {
"type": "string",
"description": "e.g. Time Magazine"
},
"summary": {
"type": "string",
"description": "e.g. Received for my work with Quantum Physics"
}
}
}
},
"publications": {
"type": "array",
"description": "Specify your publications through your career",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "e.g. The World Wide Web"
},
"publisher": {
"type": "string",
"description": "e.g. IEEE, Computer Magazine"
},
"releaseDate": {
"type": "string",
"description": "e.g. 1990-08-01"
},
"website": {
"type": "string",
"description": "e.g. http://www.computer.org/csdl/mags/co/1996/10/rx069-abs.html"
},
"summary": {
"type": "string",
"description": "Short summary of publication. e.g. Discussion of the World Wide Web, HTTP, HTML."
}
}
}
},
"skills": {
"type": "array",
"description": "List out your professional skill-set",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "e.g. Web Development"
},
"level": {
"type": "string",
"description": "e.g. Master"
},
"keywords": {
"type": "array",
"description": "List some keywords pertaining to this skill",
"additionalItems": false,
"items": {
"type": "string",
"description": "e.g. HTML"
}
}
}
}
},
"languages": {
"type": "array",
"description": "List any other languages you speak",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"language": {
"type": "string",
"description": "e.g. English, Spanish"
},
"fluency": {
"type": "string",
"description": "e.g. Fluent, Beginner"
}
}
}
},
"interests": {
"type": "array",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "e.g. Philosophy"
},
"keywords": {
"type": "array",
"additionalItems": false,
"items": {
"type": "string",
"description": "e.g. Friedrich Nietzsche"
}
}
}
}
},
"references": {
"type": "array",
"description": "List references you have received",
"additionalItems": false,
"items": {
"type": "object",
"additionalProperties": true,
"properties": {
"name": {
"type": "string",
"description": "e.g. Timothy Cook"
},
"reference": {
"type": "string",
"description": "e.g. Joe blogs was a great employee, who turned up to work at least once a week. He exceeded my expectations when it came to doing nothing."
}
}
}
}
}
}

View File

@ -1,259 +0,0 @@
/**
Abstract character/resume sheet representation.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
(function() {
var FS = require('fs')
, extend = require('../utils/extend')
, validator = require('is-my-json-valid')
, _ = require('underscore')
, PATH = require('path')
, moment = require('moment');
/**
The Sheet class represent a specific JSON character sheet. When Sheet.open
is called, we merge the loaded JSON sheet properties onto the Sheet instance
via extend(), so a full-grown sheet object will have all of the methods here,
plus a complement of JSON properties from the backing JSON file. That allows
us to treat Sheet objects interchangeably with the loaded JSON model.
@class Sheet
*/
function Sheet() {
}
/**
Open and parse the specified JSON resume sheet. Merge the JSON object model
onto this Sheet instance with extend() and convert sheet dates to a safe &
consistent format. Then sort each section by startDate descending.
*/
Sheet.prototype.open = function( file, title ) {
this.meta = { fileName: file };
this.meta.raw = FS.readFileSync( file, 'utf8' );
return this.parse( this.meta.raw, title );
};
/**
Save the sheet to disk (for environments that have disk access).
*/
Sheet.prototype.save = function( filename ) {
filename = filename || this.meta.fileName;
FS.writeFileSync( filename, this.stringify(), 'utf8' );
return this;
};
/**
Convert this object to a JSON string, sanitizing meta-properties along the
way. Don't override .toString().
*/
Sheet.prototype.stringify = function() {
function replacer( key,value ) { // Exclude these keys from stringification
return _.some(['meta', 'warnings', 'computed', 'filt', 'ctrl', 'index',
'safeStartDate', 'safeEndDate', 'safeDate', 'safeReleaseDate', 'result',
'isModified', 'htmlPreview'],
function( val ) { return key.trim() === val; }
) ? undefined : value;
}
return JSON.stringify( this, replacer, 2 );
};
/**
Open and parse the specified JSON resume sheet. Merge the JSON object model
onto this Sheet instance with extend() and convert sheet dates to a safe &
consistent format. Then sort each section by startDate descending.
*/
Sheet.prototype.parse = function( stringData, opts ) {
opts = opts || { };
var rep = JSON.parse( stringData );
extend( true, this, rep );
// Set up metadata
if( opts.meta === undefined || opts.meta ) {
this.meta = this.meta || { };
this.meta.title = (opts.title || this.meta.title) || this.basics.name;
}
// Parse dates, sort dates, and calculate computed values
(opts.date === undefined || opts.date) && _parseDates.call( this );
(opts.sort === undefined || opts.sort) && this.sort();
(opts.compute === undefined || opts.compute) && (this.computed = {
numYears: this.duration(),
keywords: this.keywords()
});
return this;
};
/**
Return a unique list of all keywords across all skills.
*/
Sheet.prototype.keywords = function() {
var flatSkills = [];
if( this.skills && this.skills.length ) {
this.skills.forEach( function( s ) {
flatSkills = _.union( flatSkills, s.keywords );
});
}
return flatSkills;
},
/**
Update the sheet's raw data. TODO: remove/refactor
*/
Sheet.prototype.updateData = function( str ) {
this.clear( false );
this.parse( str )
return this;
};
/**
Reset the sheet to an empty state.
*/
Sheet.prototype.clear = function( clearMeta ) {
clearMeta = ((clearMeta === undefined) && true) || clearMeta;
clearMeta && (delete this.meta);
delete this.computed; // Don't use Object.keys() here
delete this.work;
delete this.volunteer;
delete this.education;
delete this.awards;
delete this.publications;
delete this.interests;
delete this.skills;
};
/**
Get the default (empty) sheet.
*/
Sheet.default = function() {
return new Sheet().open( PATH.join( __dirname, 'empty.json'), 'Empty' );
}
/**
Add work experience to the sheet.
*/
Sheet.prototype.add = function( moniker ) {
var defSheet = Sheet.default();
var newObject = $.extend( true, {}, defSheet[ moniker ][0] );
this[ moniker ] = this[ moniker ] || [];
this[ moniker ].push( newObject );
return newObject;
};
/**
Determine if the sheet includes a specific social profile (eg, GitHub).
*/
Sheet.prototype.hasProfile = function( socialNetwork ) {
socialNetwork = socialNetwork.trim().toLowerCase();
return this.basics.profiles && _.some( this.basics.profiles, function(p) {
return p.network.trim().toLowerCase() === socialNetwork;
});
};
/**
Determine if the sheet includes a specific skill.
*/
Sheet.prototype.hasSkill = function( skill ) {
skill = skill.trim().toLowerCase();
return this.skills && _.some( this.skills, function(sk) {
return sk.keywords && _.some( sk.keywords, function(kw) {
return kw.trim().toLowerCase() === skill;
});
});
};
/**
Validate the sheet against the JSON Resume schema.
*/
Sheet.prototype.isValid = function( ) { // TODO: ↓ fix this path ↓
var schema = FS.readFileSync( PATH.join( __dirname, 'resume.json' ), 'utf8' );
var schemaObj = JSON.parse( schema );
var validator = require('is-my-json-valid')
var validate = validator( schemaObj );
return validate( this );
};
/**
Calculate the total duration of the sheet. Assumes this.work has been sorted
by start date descending, perhaps via a call to Sheet.sort().
@returns The total duration of the sheet's work history, that is, the number
of years between the start date of the earliest job on the resume and the
*latest end date of all jobs in the work history*. This last condition is for
sheets that have overlapping jobs.
*/
Sheet.prototype.duration = function() {
if( this.work && this.work.length ) {
var careerStart = this.work[ this.work.length - 1].safeStartDate;
if ((typeof careerStart === 'string' || careerStart instanceof String) &&
!careerStart.trim())
return 0;
var careerLast = _.max( this.work, function( w ) {
return w.safeEndDate.unix();
}).safeEndDate;
return careerLast.diff( careerStart, 'years' );
}
return 0;
};
/**
Sort dated things on the sheet by start date descending. Assumes that dates
on the sheet have been processed with _parseDates().
*/
Sheet.prototype.sort = function( ) {
this.work && this.work.sort( byDateDesc );
this.education && this.education.sort( byDateDesc );
this.volunteer && this.volunteer.sort( byDateDesc );
this.awards && this.awards.sort( function(a, b) {
return( a.safeDate.isBefore(b.safeDate) ) ? 1
: ( a.safeDate.isAfter(b.safeDate) && -1 ) || 0;
});
this.publications && this.publications.sort( function(a, b) {
return( a.safeReleaseDate.isBefore(b.safeReleaseDate) ) ? 1
: ( a.safeReleaseDate.isAfter(b.safeReleaseDate) && -1 ) || 0;
});
function byDateDesc(a,b) {
return( a.safeStartDate.isBefore(b.safeStartDate) ) ? 1
: ( a.safeStartDate.isAfter(b.safeStartDate) && -1 ) || 0;
}
};
/**
Convert human-friendly dates into formal Moment.js dates for all collections.
We don't want to lose the raw textual date as entered by the user, so we store
the Moment-ified date as a separate property with a prefix of .safe. For ex:
job.startDate is the date as entered by the user. job.safeStartDate is the
parsed Moment.js date that we actually use in processing.
*/
function _parseDates() {
var _fmt = require('./fluent-date').fmt;
this.work && this.work.forEach( function(job) {
job.safeStartDate = _fmt( job.startDate );
job.safeEndDate = _fmt( job.endDate );
});
this.education && this.education.forEach( function(edu) {
edu.safeStartDate = _fmt( edu.startDate );
edu.safeEndDate = _fmt( edu.endDate );
});
this.volunteer && this.volunteer.forEach( function(vol) {
vol.safeStartDate = _fmt( vol.startDate );
vol.safeEndDate = _fmt( vol.endDate );
});
this.awards && this.awards.forEach( function(awd) {
awd.safeDate = _fmt( awd.date );
});
this.publications && this.publications.forEach( function(pub) {
pub.safeReleaseDate = _fmt( pub.releaseDate );
});
}
/**
Export the Sheet function/ctor.
*/
module.exports = Sheet;
}());

View File

@ -1,90 +0,0 @@
/**
Abstract theme representation.
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
*/
(function() {
var FS = require('fs')
, extend = require('../utils/extend')
, validator = require('is-my-json-valid')
, _ = require('underscore')
, PATH = require('path')
, moment = require('moment');
/**
The Theme class represents a specific presentation of a resume.
@class Theme
*/
function Theme() {
}
/**
Open and parse the specified theme.
*/
Theme.prototype.open = function( themeFolder ) {
function friendlyName( val ) {
val = val.trim().toLowerCase();
var friendly = { yml: 'yaml', md: 'markdown', txt: 'text' };
return friendly[val] || val;
}
var tplFolder = PATH.join( themeFolder, 'templates' );
var fmts = FS.readdirSync( tplFolder ).map( function( file ) {
var absPath = PATH.join( tplFolder, file );
var pathInfo = PATH.parse(absPath);
var temp = [ pathInfo.name, {
title: friendlyName(pathInfo.name),
pre: pathInfo.name,
ext: pathInfo.ext.slice(1),
path: absPath,
data: FS.readFileSync( absPath, 'utf8' ),
css: null
}];
return temp;
});
// Freebie formats every theme gets
fmts.push( [ 'json', { title: 'json', pre: 'json', ext: 'json', path: null, data: null } ] );
fmts.push( [ 'yml', { title: 'yaml', pre: 'yml', ext: 'yml', path: null, data: null } ] );
// Handle CSS files
var cssFiles = fmts.filter(function( fmt ){
return fmt[1].ext === 'css';
});
cssFiles.forEach(function( cssf ) {
// For each CSS file, get its corresponding HTML file
var idx = _.findIndex(fmts, function( fmt ) { return fmt[1].pre === cssf[1].pre && fmt[1].ext === 'html' });
fmts[ idx ][1].css = cssf[1].data;
fmts[ idx ][1].cssPath = cssf[1].path;
});
fmts = fmts.filter( function( fmt) {
return fmt[1].ext !== 'css';
});
// Create a hash out of the formats for this theme
this.formats = _.object( fmts );
this.name = PATH.parse( themeFolder ).name;
return this;
};
/**
Determine if the theme supports the specified output format.
*/
Theme.prototype.hasFormat = function( fmt ) {
return _.has( this.formats, fmt );
};
/**
Determine if the theme supports the specified output format.
*/
Theme.prototype.getFormat = function( fmt ) {
return this.formats[ fmt ];
};
module.exports = Theme;
}());

View File

@ -1,148 +0,0 @@
/**
Internal resume generation logic for FluentCV.
@license Copyright (c) 2015 | James M. Devlin
*/
module.exports = function () {
// We don't mind pseudo-globals here
var path = require( 'path' )
, extend = require( './utils/extend' )
, unused = require('./utils/string')
, fs = require('fs')
, _ = require('underscore')
, FLUENT = require('./fluentlib')
, PATH = require('path')
, MKDIRP = require('mkdirp')
, rez, _log, _err;
/**
Given a source JSON resume, a destination resume path, and a theme file,
generate 0..N resumes in the desired formats.
@param src Path to the source JSON resume file: "rez/resume.json".
@param dst An array of paths to the target resume file(s).
@param theme Friendly name of the resume theme. Defaults to "modern".
@param logger Optional logging override.
*/
function gen( src, dst, opts, logger, errHandler ) {
_log = logger || console.log;
_err = errHandler || error;
//_opts = extend( true, _opts, opts );
_opts.theme = (opts.theme && opts.theme.toLowerCase().trim()) || 'modern';
_opts.prettify = opts.prettify === true ? _opts.prettify : false;
// Load input resumes...
if(!src || !src.length) { throw { fluenterror: 3 }; }
var sheets = src.map( function( res ) {
_log( 'Reading JSON resume: ' + res );
return (new FLUENT.Sheet()).open( res );
});
// Merge input resumes...
var msg = '';
rez = _.reduceRight( sheets, function( a, b, idx ) {
msg += ((idx == sheets.length - 2) ? 'Merging ' + a.meta.fileName : '')
+ ' onto ' + b.meta.fileName;
return extend( true, b, a );
});
msg && _log(msg);
// Load the active theme
// Verify the specified theme name/path
var tFolder = PATH.resolve( __dirname, '../node_modules/fluent-themes/themes', _opts.theme );
var exists = require('./utils/file-exists');
if (!exists( tFolder )) {
tFolder = PATH.resolve( _opts.theme );
if (!exists( tFolder )) {
throw { fluenterror: 1, data: _opts.theme };
}
}
var theTheme = new FLUENT.Theme().open( tFolder );
_opts.themeObj = theTheme;
_log( 'Applying ' + theTheme.name.toUpperCase() + ' theme (' + Object.keys(theTheme.formats).length + ' formats)' );
// Expand output resumes... (can't use map() here)
var targets = [];
var that = this;
( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) {
var to = path.resolve(t), pa = path.parse(to), fmat = pa.ext || '.all';
targets.push.apply(targets, fmat === '.all' ?
Object.keys( theTheme.formats ).map(function(k){ var z = theTheme.formats[k]; return { file: to.replace(/all$/g,z.pre), fmt: z } })
: [{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
});
// Run the transformation!
var finished = targets.map( function(t) { return single(t, theTheme); } );
// Don't send the client back empty-handed
return { sheet: rez, targets: targets, processed: finished };
}
/**
Generate a single resume of a specific format.
@param f Full path to the destination resume to generate, for example,
"/foo/bar/resume.pdf" or "c:\foo\bar\resume.txt".
*/
function single( fi, theme ) {
try {
var f = fi.file, fType = fi.fmt.ext, fName = path.basename( f, '.' + fType );
var fObj = _.property( fi.fmt.pre )( theme.formats );
var fOut = path.join( f.substring( 0, f.lastIndexOf('.') + 1 ) + fObj.pre );
_log( 'Generating ' + fi.fmt.title.toUpperCase() + ' resume: ' + path.relative(process.cwd(), f ) );
var theFormat = _fmts.filter( function( fmt ) {
return fmt.name === fi.fmt.pre;
})[0];
MKDIRP( path.dirname(fOut) ); // Ensure dest folder exists; don't bug user
theFormat.gen.generate( rez, fOut, _opts );
}
catch( ex ) {
_err( ex );
}
}
/**
Handle an exception.
*/
function error( ex ) {
throw ex;
}
/**
Supported resume formats.
*/
var _fmts = [
{ name: 'html', ext: 'html', gen: new FLUENT.HtmlGenerator() },
{ name: 'txt', ext: 'txt', gen: new FLUENT.TextGenerator() },
{ name: 'doc', ext: 'doc', fmt: 'xml', gen: new FLUENT.WordGenerator() },
{ name: 'pdf', ext: 'pdf', fmt: 'html', is: false, gen: new FLUENT.HtmlPdfGenerator() },
{ name: 'md', ext: 'md', fmt: 'txt', gen: new FLUENT.MarkdownGenerator() },
{ name: 'json', ext: 'json', gen: new FLUENT.JsonGenerator() },
{ name: 'yml', ext: 'yml', fmt: 'yml', gen: new FLUENT.JsonYamlGenerator() }
];
/**
Default FluentCMD options.
*/
var _opts = {
theme: 'modern',
prettify: { // ← See https://github.com/beautify-web/js-beautify#options
indent_size: 2,
unformatted: ['em','strong'],
max_char: 80, // ← See lib/html.js in above-linked repo
//wrap_line_length: 120, ← Don't use this
}
};
/**
Internal module interface. Used by FCV Desktop and HMR.
*/
return {
generate: gen,
lib: require('./fluentlib'),
options: _opts,
formats: _fmts
};
}();

View File

@ -1,17 +0,0 @@
/**
Core resume generation module for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
module.exports = {
Sheet: require('./core/sheet'),
Theme: require('./core/theme'),
HtmlGenerator: require('./gen/html-generator'),
TextGenerator: require('./gen/text-generator'),
HtmlPdfGenerator: require('./gen/html-pdf-generator'),
WordGenerator: require('./gen/word-generator'),
MarkdownGenerator: require('./gen/markdown-generator'),
JsonGenerator: require('./gen/json-generator'),
YamlGenerator: require('./gen/yaml-generator'),
JsonYamlGenerator: require('./gen/json-yaml-generator')
};

View File

@ -1,43 +0,0 @@
/**
Base resume generator for FluentCV.
@license Copyright (c) 2015 | James M. Devlin
*/
(function() {
// Use J. Resig's nifty class implementation
var Class = require( '../utils/class' );
/**
The BaseGenerator class is the root of the generator hierarchy. Functionality
common to ALL generators lives here.
*/
var BaseGenerator = module.exports = Class.extend({
/**
Base-class initialize.
*/
init: function( outputFormat ) {
this.format = outputFormat;
},
/**
Status codes.
*/
codes: {
success: 0,
themeNotFound: 1,
copyCss: 2,
resumeNotFound: 3
},
/**
Generator options.
*/
opts: {
}
});
}());

View File

@ -1,32 +0,0 @@
/**
HTML resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
var FS = require('fs-extra');
var HTML = require( 'html' );
var HtmlGenerator = module.exports = TemplateGenerator.extend({
init: function() {
this._super( 'html' );
},
/**
Generate an HTML resume with optional pretty printing.
*/
onBeforeSave: function( mk, theme, outputFile ) {
var themeFile = theme.getFormat('html').path;
var cssSrc = themeFile.replace( /.html$/g, '.css' );
var cssDst = outputFile.replace( /.html$/g, '.css' );
var that = this;
FS.copySync( cssSrc, cssDst, { clobber: true }, function( e ) {
throw { fluenterror: that.codes.copyCss, data: [cssSrc,cssDst] };
});
return this.opts.prettify ?
HTML.prettyPrint( mk, this.opts.prettify ) : mk;
}
});

View File

@ -1,74 +0,0 @@
/**
HTML-based PDF resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
var FS = require('fs-extra');
var HTML = require( 'html' );
var HtmlPdfGenerator = module.exports = TemplateGenerator.extend({
init: function() {
this._super( 'pdf', 'html' );
},
/**
Generate an HTML resume with optional pretty printing.
TODO: Avoid copying the CSS file to dest if we don't need to.
*/
onBeforeSave: function( mk, themeFile, outputFile ) {
// var cssSrc = themeFile.replace( /pdf\.html$/gi, 'html.css' );
// var cssDst = outputFile.replace( /\.pdf$/gi, '.css' );
// var that = this;
// FS.copySync( cssSrc, cssDst, { clobber: true }, function( e ) {
// if( e ) that.err( "Couldn't copy CSS file to destination: " + e);
// });
// return true ?
// HTML.prettyPrint( mk, { indent_size: 2 } ) : mk;
pdf(mk, outputFile);
return mk;
}
});
/**
Generate a PDF from HTML.
*/
function pdf( markup, fOut ) {
var pdfCount = 0;
if( false ) { //( _opts.pdf === 'phantom' || _opts.pdf == 'all' ) {
pdfCount++;
require('phantom').create( function( ph ) {
ph.createPage( function( page ) {
page.setContent( markup );
page.set('paperSize', {
format: 'A4',
orientation: 'portrait',
margin: '1cm'
});
page.set("viewportSize", {
width: 1024, // TODO: option-ify
height: 768 // TODO: Use "A" sizes
});
page.set('onLoadFinished', function(success) {
page.render( fOut );
pdfCount++;
ph.exit();
});
},
{ dnodeOpts: { weak: false } } );
});
}
if( true ) { // _opts.pdf === 'wkhtmltopdf' || _opts.pdf == 'all' ) {
var fOut2 = fOut;
if( pdfCount == 1 ) {
fOut2 = fOut2.replace(/\.pdf$/g, '.b.pdf');
}
require('wkhtmltopdf')( markup, { pageSize: 'letter' } )
.pipe( FS.createWriteStream( fOut2 ) );
pdfCount++;
}
}

View File

@ -1,35 +0,0 @@
/**
Definition of the JsonGenerator class.
@license Copyright (c) 2015 | James M. Devlin
*/
var BaseGenerator = require('./base-generator');
var FS = require('fs');
var _ = require('underscore');
/**
The JsonGenerator generates a JSON resume directly.
*/
var JsonGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'json' );
},
invoke: function( rez ) {
// TODO: merge with FCVD
function replacer( key,value ) { // Exclude these keys from stringification
return _.some(['meta', 'warnings', 'computed', 'filt', 'ctrl', 'index',
'safeStartDate', 'safeEndDate', 'safeDate', 'safeReleaseDate', 'result',
'isModified', 'htmlPreview'],
function( val ) { return key.trim() === val; }
) ? undefined : value;
}
return JSON.stringify( rez, replacer, 2 );
},
generate: function( rez, f ) {
FS.writeFileSync( f, this.invoke(rez), 'utf8' );
}
});

View File

@ -1,37 +0,0 @@
/**
A JSON-driven YAML resume generator for FluentLib.
@module json-yaml-generator.js
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk
*/
(function() {
var BaseGenerator = require('./base-generator');
var FS = require('fs');
var YAML = require('yamljs');
/**
JsonYamlGenerator takes a JSON resume object and translates it directly to
JSON without a template, producing an equivalent YAML-formatted resume. See
also YamlGenerator (yaml-generator.js).
*/
var JsonYamlGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'yml' );
},
invoke: function( rez, themeMarkup, cssInfo, opts ) {
return YAML.stringify( JSON.parse( rez.stringify() ), Infinity, 2 );
},
generate: function( rez, f, opts ) {
var data = YAML.stringify( JSON.parse( rez.stringify() ), Infinity, 2 );
FS.writeFileSync( f, data, 'utf8' );
}
});
}());

View File

@ -1,17 +0,0 @@
/**
Markdown resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
/**
MarkdownGenerator generates a Markdown-formatted resume via TemplateGenerator.
*/
var MarkdownGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'md', 'txt' );
}
});

View File

@ -1,173 +0,0 @@
/**
Template-based resume generator base for FluentCV.
@license Copyright (c) 2015 | James M. Devlin
*/
var FS = require( 'fs' )
, _ = require( 'underscore' )
, MD = require( 'marked' )
, XML = require( 'xml-escape' )
, PATH = require('path')
, BaseGenerator = require( './base-generator' )
, EXTEND = require('../utils/extend')
, Theme = require('../core/theme');
// Default options.
var _defaultOpts = {
themeRelative: '../../node_modules/fluent-themes/themes',
keepBreaks: true,
freezeBreaks: true,
nSym: '&newl;', // newline entity
rSym: '&retn;', // return entity
template: {
interpolate: /\{\{(.+?)\}\}/g,
escape: /\{\{\=(.+?)\}\}/g,
evaluate: /\{\%(.+?)\%\}/g,
comment: /\{\#(.+?)\#\}/g
},
filters: {
out: function( txt ) { return txt; },
raw: function( txt ) { return txt; },
xml: function( txt ) { return XML(txt); },
md: function( txt ) { return MD(txt); },
mdin: function( txt ) { return MD(txt).replace(/^\s*\<p\>|\<\/p\>\s*$/gi, ''); },
lower: function( txt ) { return txt.toLowerCase(); }
},
prettify: { // ← See https://github.com/beautify-web/js-beautify#options
indent_size: 2,
unformatted: ['em','strong'],
max_char: 80, // ← See lib/html.js in above-linked repo
//wrap_line_length: 120, <-- Don't use this
}
};
/**
TemplateGenerator performs resume generation via Underscore-style template
expansion and is appropriate for text-based formats like HTML, plain text,
and XML versions of Microsoft Word, Excel, and OpenOffice.
*/
var TemplateGenerator = module.exports = BaseGenerator.extend({
/** outputFormat: html, txt, pdf, doc
templateFormat: html or txt
**/
init: function( outputFormat, templateFormat, cssFile ){
this._super( outputFormat );
this.tplFormat = templateFormat || outputFormat;
},
/** Default generation method for template-based generators. */
invoke: function( rez, themeMarkup, cssInfo, opts ) {
// Compile and invoke the template!
this.opts = EXTEND( true, {}, _defaultOpts, opts );
mk = this.single( rez, themeMarkup, this.format, cssInfo, { } );
this.onBeforeSave && (mk = this.onBeforeSave( mk, themeFile, f ));
return mk;
},
/** Default generation method for template-based generators. */
generate: function( rez, f, opts ) {
// Carry over options
this.opts = EXTEND( true, { }, _defaultOpts, opts );
// Verify the specified theme name/path
var tFolder = PATH.resolve( __dirname, this.opts.themeRelative, this.opts.theme );
var exists = require('../utils/file-exists');
if (!exists( tFolder )) {
tFolder = PATH.resolve( this.opts.theme );
if (!exists( tFolder )) {
throw { fluenterror: this.codes.themeNotFound, data: this.opts.theme };
}
}
// Load the theme
var theme = opts.themeObj || new Theme().open( tFolder );
// Load theme and CSS data
var tplFolder = PATH.join( tFolder, 'templates' );
var curFmt = theme.getFormat( this.format );
var ctx = { file: curFmt.css ? curFmt.cssPath : null, data: curFmt.css || null };
// Compile and invoke the template!
var mk = this.single( rez, curFmt.data, this.format, ctx, opts );
this.onBeforeSave && (mk = this.onBeforeSave( mk, theme, f ));
FS.writeFileSync( f, mk, { encoding: 'utf8', flags: 'w' } );
},
/**
Perform a single resume JSON-to-DEST resume transformation. Exists as a
separate function in order to expose string-based transformations to clients
who don't have access to filesystem resources (in-browser, etc.).
*/
single: function( json, jst, format, cssInfo, opts ) {
// Freeze whitespace in the template.
this.opts.freezeBreaks && ( jst = freeze(jst) );
// Tweak underscore's default template delimeters
_.templateSettings = this.opts.template;
// Convert {{ someVar }} to {% print(filt.out(someVar) %}
// Convert {{ someVar|someFilter }} to {% print(filt.someFilter(someVar) %}
jst = jst.replace( _.templateSettings.interpolate, function replace(m, p1) {
if( p1.indexOf('|') > -1 ) {
var terms = p1.split('|');
return '{% print( filt.' + terms[1] + '( ' + terms[0] + ' )) %}';
}
else {
return '{% print( filt.out(' + p1 + ') ) %}';
}
});
// Strip {# comments #}
jst = jst.replace( _.templateSettings.comment, '');
json.display_progress_bar = true;
// Compile and run the template. TODO: avoid unnecessary recompiles.
jst = _.template(jst)({ r: json, filt: this.opts.filters, cssInfo: cssInfo, headFragment: this.opts.headFragment || '' });
// Unfreeze whitespace
this.opts.freezeBreaks && ( jst = unfreeze(jst) );
return jst;
}
});
/**
Export the TemplateGenerator function/ctor.
*/
module.exports = TemplateGenerator;
/**
Freeze newlines for protection against errant JST parsers.
*/
function freeze( markup ) {
return markup
.replace( _reg.regN, _defaultOpts.nSym )
.replace( _reg.regR, _defaultOpts.rSym );
}
/**
Unfreeze newlines when the coast is clear.
*/
function unfreeze( markup ) {
return markup
.replace( _reg.regSymR, '\r' )
.replace( _reg.regSymN, '\n' );
}
/**
Regexes for linebreak preservation.
*/
var _reg = {
regN: new RegExp( '\n', 'g' ),
regR: new RegExp( '\r', 'g' ),
regSymN: new RegExp( _defaultOpts.nSym, 'g' ),
regSymR: new RegExp( _defaultOpts.rSym, 'g' )
};

View File

@ -1,19 +0,0 @@
/**
Plain text resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
/**
The TextGenerator generates a plain-text resume via the TemplateGenerator.
*/
var TextGenerator = TemplateGenerator.extend({
init: function(){
this._super( 'txt' );
},
});
module.exports = TextGenerator;

View File

@ -1,13 +0,0 @@
/**
MS Word resume generator for FluentCV.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
var TemplateGenerator = require('./template-generator');
var WordGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'doc', 'xml' );
},
});

View File

@ -1,17 +0,0 @@
/**
XML resume generator for FluentCV.
@license Copyright (c) 2015 | James M. Devlin
*/
var BaseGenerator = require('./base-generator');
/**
The XmlGenerator generates an XML resume via the TemplateGenerator.
*/
var XmlGenerator = module.exports = BaseGenerator.extend({
init: function(){
this._super( 'xml' );
},
});

View File

@ -1,24 +0,0 @@
/**
A YAML resume generator for FluentLib.
@module yaml-generator.js
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk
*/
(function() {
var TemplateGenerator = require('./template-generator');
/**
YamlGenerator generates a YAML-formatted resume via TemplateGenerator.
*/
var YamlGenerator = module.exports = TemplateGenerator.extend({
init: function(){
this._super( 'yml', 'yml' );
}
});
}());

View File

@ -1,78 +1,22 @@
#! /usr/bin/env node
/**
Command-line interface (CLI) for FluentCV via Node.js.
@license Copyright (c) 2015 | James M. Devlin
*/
var ARGS = require( 'minimist' )
, FCMD = require( './fluentcmd')
, PKG = require('../package.json')
, opts = { };
/**
Command-line interface (CLI) for HackMyResume.
@license MIT. See LICENSE.md for details.
@module index.js
*/
try {
main();
require('./cli/main')( process.argv );
}
catch( ex ) {
handleError( ex );
}
function main() {
// Setup.
var title = '*** FluentCV v' + PKG.version + ' ***';
if( process.argv.length <= 2 ) { logMsg(title); throw { fluenterror: 3 }; }
var args = ARGS( process.argv.slice(2) );
opts = getOpts( args );
logMsg( title );
// Convert arguments to source files, target files, options
var src = args._ || [];
var dst = (args.o && ((typeof args.o === 'string' && [ args.o ]) || args.o)) || [];
dst = (dst === true) ? [] : dst; // Handle -o with missing output file
// Generate!
FCMD.generate( src, dst, opts, logMsg );
process.exit(0);
}
function logMsg( msg ) {
opts.silent || console.log( msg );
}
function getOpts( args ) {
var noPretty = args['nopretty'] || args.n;
noPretty = noPretty && (noPretty === true || noPretty === 'true');
return {
theme: args.t || 'modern',
prettify: !noPretty,
silent: args.s || args.silent
};
}
function handleError( ex ) {
var msg = '', exitCode;
if( ex.fluenterror ){
switch( ex.fluenterror ) { // TODO: Remove magic numbers
case 1: msg = "The specified theme couldn't be found: " + ex.data; break;
case 2: msg = "Couldn't copy CSS file to destination folder"; break;
case 3: msg = "Please specify a valid JSON resume file."; break;
};
exitCode = ex.fluenterror;
}
else {
msg = ex.toString();
exitCode = 4;
}
var idx = msg.indexOf('Error: ');
var trimmed = idx === -1 ? msg : msg.substring( idx + 7 );
console.log( 'ERROR: ' + trimmed.toString() );
process.exit( exitCode );
require('./cli/error').err( ex, true );
}

View File

@ -1,67 +0,0 @@
/* Simple JavaScript Inheritance
* By John Resig http://ejohn.org/
* MIT Licensed.
* http://ejohn.org/blog/simple-javascript-inheritance/
*/
// Inspired by base2 and Prototype
(function(){
var initializing = false, fnTest = /xyz/.test(function(){xyz;}) ? /\b_super\b/ : /.*/;
// The base Class implementation (does nothing)
this.Class = function(){};
module.exports = Class;
// Create a new Class that inherits from this class
Class.extend = function(prop) {
var _super = this.prototype;
// Instantiate a base class (but only create the instance,
// don't run the init constructor)
initializing = true;
var prototype = new this();
initializing = false;
// Copy the properties over onto the new prototype
for (var name in prop) {
// Check if we're overwriting an existing function
prototype[name] = typeof prop[name] == "function" &&
typeof _super[name] == "function" && fnTest.test(prop[name]) ?
(function(name, fn){
return function() {
var tmp = this._super;
// Add a new ._super() method that is the same method
// but on the super-class
this._super = _super[name];
// The method only need to be bound temporarily, so we
// remove it when we're done executing
var ret = fn.apply(this, arguments);
this._super = tmp;
return ret;
};
})(name, prop[name]) :
prop[name];
}
// The dummy class constructor
function Class() {
// All construction is actually done in the init method
if ( !initializing && this.init )
this.init.apply(this, arguments);
}
// Populate our constructed prototype object
Class.prototype = prototype;
// Enforce the constructor to be what we expect
Class.prototype.constructor = Class;
// And make this class extendable
Class.extend = arguments.callee;
return Class;
};
})();

View File

@ -1,78 +0,0 @@
/**
Plain JavaScript replacement of jQuery .extend based on jQuery sources.
@license Copyright (c) 2015 by James M. Devlin. All rights reserved.
*/
function _extend() {
function isPlainObject( obj ) {
if ((typeof obj !== "object") || obj.nodeType ||
(obj !== null && obj === obj.window)) {
return false;
}
if (obj.constructor &&
!hasOwnProperty.call( obj.constructor.prototype, "isPrototypeOf" )) {
return false;
}
return true;
}
var options
, name
, src
, copy
, copyIsArray
, clone
, target = arguments[0] || {}
, i = 1
, length = arguments.length
, deep = false;
// Handle a deep copy situation
if (typeof target === "boolean") {
deep = target;
// Skip the boolean and the target
target = arguments[i] || {};
i++;
}
// Handle case when target is a string or something (possible in deep copy)
//if (typeof target !== "object" && !jQuery.isFunction(target))
if (typeof target !== "object" && typeof target !== "function")
target = {};
for (; i < length; i++) {
// Only deal with non-null/undefined values
if ((options = arguments[i]) !== null) {
// Extend the base object
for (name in options) {
src = target[name];
copy = options[name];
// Prevent never-ending loop
if (target === copy) continue;
// Recurse if we're merging plain objects or arrays
if (deep && copy && (isPlainObject(copy) ||
(copyIsArray = (copy.constructor === Array)))) {
if (copyIsArray) {
copyIsArray = false;
clone = src && (src.constructor === Array) ? src : [];
} else {
clone = src && isPlainObject(src) ? src : {};
}
// Never move original objects, clone them
target[name] = _extend(deep, clone, copy);
// Don't bring in undefined values
} else if (copy !== undefined) {
target[name] = copy;
}
}
}
}
// Return the modified object
return target;
}
module.exports = _extend;

View File

@ -1,18 +0,0 @@
/**
File-exists checker for Node.js.
@license Copyright (c) 2015 | James M. Devlin
*/
var FS = require('fs');
// Yup, this is now the recommended way to check for file existence on Node.
// fs.exists is deprecated and the recommended fs.statSync/lstatSync throws
// exceptions on non-existent paths :)
module.exports = function (path) {
try {
FS.statSync( path );
return true;
} catch( err ) {
return !(err && err.code === 'ENOENT');
}
};

View File

@ -1,18 +0,0 @@
/**
String utility functions.
@license Copyright (c) 2015 | James M. Devlin
*/
/**
Determine if the string is null, empty, or whitespace.
See: http://stackoverflow.com/a/32800728/4942583
@method isNullOrWhitespace
*/
String.isNullOrWhitespace = function( input ) {
return !input || !input.trim();
};
String.prototype.endsWith = function(suffix) {
return this.indexOf(suffix, this.length - suffix.length) !== -1;
};

1
test/.gitignore vendored Normal file
View File

@ -0,0 +1 @@
sandbox/

4
test/hmr-options.json Normal file
View File

@ -0,0 +1,4 @@
{
"theme": "positive",
"debug": true
}

View File

@ -0,0 +1,91 @@
{
"basics": {
"name": "",
"label": "",
"picture": "",
"email": "",
"phone": "",
"degree": "",
"website": "",
"summary": "",
"location": {
"address": "",
"postalCode": "",
"city": "",
"countryCode": "",
"region": ""
},
"profiles": [
{
"network": "",
"username": "",
"url": ""
}
]
},
"work": [
{
"company": "",
"position": "",
"website": "",
"startDate": "",
"endDate": "",
"summary": "",
"highlights": [
""
]
}
],
"awards": [
{
"title": "",
"date": "",
"awarder": "",
"summary": ""
}
],
"education": [
{
"institution": "",
"area": "",
"studyType": "",
"startDate": "",
"endDate": "",
"gpa": "",
"courses": [
""
]
}
],
"publications": [
{
"name": "",
"publisher": "",
"releaseDate": "",
"website": "",
"summary": ""
}
],
"volunteer": [
{
"organization": "",
"position": "",
"website": "",
"startDate": "",
"endDate": "",
"summary": "",
"highlights": [
""
]
}
],
"skills": [
{
"name": "",
"level": "",
"keywords": [
""
]
}
]
}

View File

@ -0,0 +1,104 @@
{
"basics": {
"name": "Jane Q. Fullstacker"
},
"education": [
{
"gpa": "3.5",
"courses": [
"Course 1",
"Course 2",
"Course 2"
],
"startDate": "2005-09",
"endDate": "2008-05"
},
{
"institution": "Medfield College",
"gpa": "3.2",
"courses": [
"Course 1",
"Course 2",
"Course 2"
],
"startDate": "2003-09",
"endDate": "2005-06"
}
],
"skills": [
{
"name": "Web Dev",
"keywords": [
"JavaScript",
"HTML 5",
"CSS",
"LAMP",
"MVC",
"REST"
]
},
{
"name": "JavaScript"
}
],
"volunteer": [],
"publications": [
{
"name": "Building User Interfaces with Electron and Atom",
"releaseDate": "2011",
"website": "http://codeproject.com/build-ui-electron-atom.aspx"
},
{
"name": "Jane Fullstacker's Blog",
"publisher": "self",
"releaseDate": "2011",
"website": "http://janef.me"
},
{
"name": "Teach Yourself GORFF in 21 Days",
"publisher": "Amazon"
}
],
"interests": [
{
"name": "reading",
"summary": "Jane is a fan of mystery novels and courtroom dramas including Agatha Christie and John Grisham.",
"keywords": [
"mystery",
"Agatha Christie",
"John Grisham"
]
},
{
"name": "hiking",
"summary": "Jane enjoys hiking, light mountain climbing, and has four summits under her belt!"
},
{
"name": "yoga"
}
],
"references": [
{
"name": "John Davidson",
"reference": "Jane is awesome! I'd hire her again in a heartbeat."
},
{
"name": "Elias Fullstacker",
"reference": "I worked with Jane on Jabberwocky and can vouch for her awesome technical capabilities and attention to detail. Insta-hire."
},
{
"name": "Dana Nevins",
"reference": "I've known Jane personally and professionally for almost ten years. She is one in a million."
}
],
"languages": [
{
"language": "English",
"level": "Native"
},
{
"language": "Spanish",
"level": "Moderate"
}
]
}

View File

@ -0,0 +1,263 @@
{
"basics": {
"name": "Jane Q. Fullstacker",
"label": "Senior Developer",
"summary": "**Full-stack software developer with 6+ years industry experience** specializing in scalable cloud architectures for this, that, and the other. A native of southern CA, Jane enjoys hiking, mystery novels, and the company of Rufus, her two-year-old beagle.",
"website": "http://janef.me/blog",
"phone": "1-650-999-7777",
"email": "jdoe@onecoolstartup.io",
"picture": "jane_doe.png",
"location": {
"address": "Jane Fullstacker\n123 Somewhere Rd.\nMountain View, CA 94035",
"postalCode": "94035",
"city": "Mountain View",
"countryCode": "US",
"region": "CA"
},
"profiles": [
{
"network": "GitHub",
"username": "janef-was-here",
"url": "https://github.com/janef-was-here"
},
{
"network": "Twitter",
"username": "janef-was-here",
"url": "https://twitter.com/janef-was-here"
}
]
},
"work": [
{
"company": "One Cool Startup",
"website": "https://onecool.io/does-not-exist",
"position": "Head Code Ninja",
"startDate": "2013-09",
"summary": "Development team manager for OneCoolApp and OneCoolWebsite, a free social network tiddlywink generator and lifestyle portal with over 200,000 users.",
"highlights": [
"Managed a 5-person development team",
"Accomplishment 2",
"Etc."
]
},
{
"company": "Veridian Dynamics",
"website": "https://en.wikipedia.org/wiki/Better_Off_Ted#Plot",
"position": "Principal Developer",
"startDate": "2011-07",
"endDate": "2013-08",
"summary": "Developer on numerous projects culminating in technical lead role for the [Jabberwocky project](http://betteroffted.wikia.com/wiki/Jabberwocky) and promotion to principal developer.",
"highlights": [
"Managed a 5-person development team",
"Accomplishment 2",
"Etc."
]
},
{
"company": "Stark Industries",
"position": "IT Administrator",
"startDate": "2008-10",
"endDate": "2011-06",
"summary": "Junior programmer with heavy code responsibilities. Promoted to intermediate role after 6 months.",
"highlights": [
"Promoted to intermediate developer after 6 months",
"Accomplishment 2",
"Etc."
]
},
{
"company": "Dunder Mifflin",
"position": "Intern",
"startDate": "2008-06",
"endDate": "2008-09",
"summary": "Performed IT administration and deployments for Dunder Mifflin.",
"highlights": [
"Supervised roll-out of Dunder Mifflin Infinity website.",
"Performed mission-critical system backups and ",
"Etc."
]
}
],
"education": [
{
"institution": "Cornell University",
"gpa": "3.5",
"courses": [
"Course 1",
"Course 2",
"Course 2"
],
"startDate": "2005-09",
"endDate": "2008-05"
},
{
"institution": "Medfield College",
"gpa": "3.2",
"courses": [
"Course 1",
"Course 2",
"Course 2"
],
"startDate": "2003-09",
"endDate": "2005-06"
}
],
"skills": [
{
"name": "Web Dev",
"keywords": [
"JavaScript",
"HTML 5",
"CSS",
"LAMP",
"MVC",
"REST"
]
},
{
"name": "JavaScript",
"keywords": [
"Node.js",
"Angular.js",
"jQuery",
"Bootstrap",
"React.js",
"Backbone.js"
]
},
{
"name": "Database",
"keywords": [
"MySQL",
"PostgreSQL",
"NoSQL",
"ORM",
"Hibernate"
]
},
{
"name": "Cloud",
"keywords": [
"AWS",
"EC2",
"RDS",
"S3",
"Azure",
"Dropbox"
]
},
{
"name": "Project",
"keywords": [
"Agile",
"TFS",
"Unified Process",
"MS Project"
]
}
],
"volunteer": [
{
"organization": "Technology for Tots",
"position": "Technical Consultant",
"startDate": "2003-11",
"endDate": "2005-06",
"website": "http://technology-for-tots.org",
"summary": "Summary of this volunteer stint.",
"highlights": [
"Accomplishment 1",
"Accomplishment 2",
"etc"
]
},
{
"organization": "US Army Reserves",
"position": "NCO",
"startDate": "1999-11",
"endDate": "2003-06",
"website": "http://www.usar.army.mil/",
"summary": "Summary of this military stint.",
"highlights": [
"Accomplishment 1",
"Accomplishment 2",
"etc"
]
}
],
"awards": [
{
"title": "Honorable Mention",
"date": "2012",
"awarder": "Google"
},
{
"title": "Summa cum laude",
"date": "2012",
"awarder": "Cornell University"
}
],
"publications": [
{
"name": "Building User Interfaces with Electron and Atom",
"publisher": "Code Project",
"releaseDate": "2011",
"website": "http://codeproject.com/build-ui-electron-atom.aspx"
},
{
"name": "Jane Fullstacker's Blog",
"publisher": "self",
"releaseDate": "2011",
"website": "http://janef.me"
},
{
"name": "Teach Yourself GORFF in 21 Days",
"publisher": "Amazon",
"releaseDate": "2008",
"website": "http://url.to.publication.com/blah",
"summary": "A primer on the programming language of GORFF, whose for loops are coterminous with all of time and space."
}
],
"interests": [
{
"name": "reading",
"summary": "Jane is a fan of mystery novels and courtroom dramas including Agatha Christie and John Grisham.",
"keywords": [
"mystery",
"Agatha Christie",
"John Grisham"
]
},
{
"name": "hiking",
"summary": "Jane enjoys hiking, light mountain climbing, and has four summits under her belt!"
},
{
"name": "yoga"
}
],
"references": [
{
"name": "John Davidson",
"reference": "Jane is awesome! I'd hire her again in a heartbeat."
},
{
"name": "Elias Fullstacker",
"reference": "I worked with Jane on Jabberwocky and can vouch for her awesome technical capabilities and attention to detail. Insta-hire."
},
{
"name": "Dana Nevins",
"reference": "I've known Jane personally and professionally for almost ten years. She is one in a million."
}
],
"languages": [
{
"language": "English",
"level": "Native"
},
{
"language": "Spanish",
"level": "Moderate",
"years": 10
}
]
}

View File

@ -0,0 +1,130 @@
{
"basics": {
"name": "Richard Hendriks",
"label": "Programmer",
"picture": "",
"email": "richard.hendriks@gmail.com",
"phone": "(912) 555-4321",
"website": "http://richardhendricks.com",
"summary": "Richard hails from Tulsa. He has earned degrees from the University of Oklahoma and Stanford. (Go Sooners and Cardinals!) Before starting Pied Piper, he worked for Hooli as a part time software developer. While his work focuses on applied information theory, mostly optimizing lossless compression schema of both the length-limited and adaptive variants, his non-work interests range widely, everything from quantum computing to chaos theory. He could tell you about it, but THAT would NOT be a “length-limited” conversation!",
"location": {
"address": "2712 Broadway St",
"postalCode": "CA 94115",
"city": "San Francisco",
"countryCode": "US",
"region": "California"
},
"profiles": [
{
"network": "Twitter",
"username": "neutralthoughts",
"url": ""
},
{
"network": "SoundCloud",
"username": "dandymusicnl",
"url": "https://soundcloud.com/dandymusicnl"
}
]
},
"work": [
{
"company": "Pied Piper",
"position": "CEO/President",
"website": "http://piedpiper.com",
"startDate": "2013-12-01",
"endDate": "2014-12-01",
"summary": "Pied Piper is a multi-platform technology based on a proprietary universal compression algorithm that has consistently fielded high Weisman Scores™ that are not merely competitive, but approach the theoretical limit of lossless compression.",
"highlights": [
"Build an algorithm for artist to detect if their music was violating copy right infringement laws",
"Successfully won Techcrunch Disrupt",
"Optimized an algorithm that holds the current world record for Weisman Scores"
]
}
],
"volunteer": [
{
"organization": "CoderDojo",
"position": "Teacher",
"website": "http://coderdojo.com/",
"startDate": "2012-01-01",
"endDate": "2013-01-01",
"summary": "Global movement of free coding clubs for young people.",
"highlights": [
"Awarded 'Teacher of the Month'"
]
}
],
"education": [
{
"institution": "University of Oklahoma",
"area": "Information Technology",
"studyType": "Bachelor",
"startDate": "2011-06-01",
"endDate": "2014-01-01",
"gpa": "4.0",
"courses": [
"DB1101 - Basic SQL",
"CS2011 - Java Introduction"
]
}
],
"awards": [
{
"title": "Digital Compression Pioneer Award",
"date": "2014-11-01",
"awarder": "Techcrunch",
"summary": "There is no spoon."
}
],
"publications": [
{
"name": "Video compression for 3d media",
"publisher": "Hooli",
"releaseDate": "2014-10-01",
"website": "http://en.wikipedia.org/wiki/Silicon_Valley_(TV_series)",
"summary": "Innovative middle-out compression algorithm that changes the way we store data."
}
],
"skills": [
{
"name": "Web Development",
"level": "Master",
"keywords": [
"HTML",
"CSS",
"Javascript"
]
},
{
"name": "Compression",
"level": "Master",
"keywords": [
"Mpeg",
"MP4",
"GIF"
]
}
],
"languages": [
{
"language": "English",
"fluency": "Native speaker"
}
],
"interests": [
{
"name": "Wildlife",
"keywords": [
"Ferrets",
"Unicorns"
]
}
],
"references": [
{
"name": "Erlich Bachman",
"reference": "It is my pleasure to recommend Richard, his performance working as a consultant for Main St. Company proved that he will be a valuable addition to any company."
}
]
}

76
test/test-cli.js Normal file
View File

@ -0,0 +1,76 @@
/**
CLI test routines for HackMyResume.
@module test-cli.js
*/
var chai = require('chai')
, should = chai.should()
, HMRMAIN = require('../src/cli/main')
, CHALK = require('chalk')
, FS = require('fs')
, PATH = require('path')
, PKG = require('../package.json')
, _ = require('underscore');
var gather = '';
var ConsoleLogOrg = console.log;
var ProcessExitOrg = process.exit;
var commandRetVal = 0;
describe('Testing CLI interface', function () {
// TODO: use sinon
// Replacement for process.exit()
function MyProcessExit( retVal ) {
commandRetVal = retVal;
}
// HackMyResume CLI stub. Handle a single HMR invocation.
function HackMyResumeStub( argsString ) {
var args = argsString.split(' ');
args.unshift( process.argv[1] );
args.unshift( process.argv[0] );
process.exit = MyProcessExit;
try {
var HMRMAIN = require('../src/cli/main');
HMRMAIN( args );
}
catch( ex ) {
require('../src/cli/error').err( ex, false );
//if(ex.stack || (ex.inner && ex.inner.stacl))
//console.log(ex.stack || ex.inner.stack);
}
process.exit = ProcessExitOrg;
}
// Run a test through the stub, gathering console.log output into "gather"
// and testing against it.
function run( args, expErr ) {
var title = args;
it( 'Testing: "' + title + '"\n\n', function() {
commandRetVal = 0;
HackMyResumeStub( args );
commandRetVal.should.equal( parseInt(expErr, 10) );
});
}
var lines = FS.readFileSync( PATH.join( __dirname, './test-hmr.txt'), 'utf8').split('\n');
lines.forEach(function(l){
if( l && l.trim() ) {
if(l[0] !== '#') {
var lineInfo = l.split('|');
var errCode = lineInfo[0];
run( lineInfo.length > 1 ? lineInfo[1] : '', errCode );
}
}
});
});

27
test/test-hmr.txt Normal file
View File

@ -0,0 +1,27 @@
0|
0|--help
0|-h
0|--debug
0|-d
5|notacommand
3|build
14|build doesnt-exist.json
14|build doesnt-exist.json -t not-a-theme
14|build doesnt-exist.json -t node_modules/not-a-theme
8|new
0|new test/sandbox/cli-test/new-empty-resume.auto.json
0|new test/sandbox/cli-test/new-empty-resume.jrs.json -f jrs
0|new test/sandbox/cli-test/new-empty-resume.fresh.json -f fresh
3|analyze
14|analyze doesnt-exist.json
3|convert
7|convert doesnt-exist.json
3|validate
14|validate doesnt-exist.json
0|validate node_modules/fresh-test-resumes/src/fresh/jane-fullstacker.json
3|peek
14|peek doesnt-exist.json
14|peek doesnt-exist.json not.a.path
0|peek test/resumes/jrs-0.0.0/richard-hendriks.json work[0]
0|peek node_modules/fresh-test-resumes/src/fresh/jane-fullstacker.json employment.history[1]
0|peek node_modules/fresh-test-resumes/src/fresh/johnny-trouble.json skills.sets

144
test/test-stdout.js Normal file
View File

@ -0,0 +1,144 @@
/**
Output test routines for HackMyResume.
@module test-stdout.js
*/
var chai = require('chai')
, expect = chai.expect
, HMRMAIN = require('../src/cli/main')
, CHALK = require('chalk')
, FS = require('fs')
, PATH = require('path')
, PKG = require('../package.json')
, _ = require('underscore');
var gather = '';
var ConsoleLogOrg = console.log;
var ProcessExitOrg = process.exit;
describe('Testing Ouput interface', function () {
// TODO: use sinon
// Replacement for console.log
function MyConsoleLog( msg ) {
gather += Array.prototype.slice.call(arguments).join(' ');
ConsoleLogOrg.apply(this, arguments);
}
// Replacement for process.exit()
function MyProcessExit() {
}
// HackMyResume CLI stub. Handle a single HMR invocation.
function HackMyResumeStub( args ) {
console.log = MyConsoleLog;
process.exit = MyProcessExit;
CHALK.enabled = false;
try {
args.unshift( process.argv[1] );
args.unshift( process.argv[0] );
var HMRMAIN = require('../src/cli/main');
HMRMAIN( args );
}
catch( ex ) {
require('../src/cli/error').err( ex, false );
}
CHALK.enabled = true;
process.exit = ProcessExitOrg;
console.log = ConsoleLogOrg;
}
// Run a test through the stub, gathering console.log output into "gather"
// and testing against it.
function run( title, args, tests ) {
it( title, function() {
gather = '';
HackMyResumeStub( args );
expect(
_.all( tests, function(t) {
return gather.indexOf(t) > -1;
})
).to.equal(true);
});
}
var title = '*** HackMyResume v' + PKG.version + ' ***';
var feedMe = 'Please feed me a resume in FRESH or JSON Resume format.';
var manPage = FS.readFileSync( PATH.resolve( __dirname, '../src/cli/use.txt' ), 'utf8');
run('HMR should output a help string when no command is specified',
[], [ title, 'Please give me a command (BUILD, ANALYZE, VALIDATE, CONVERT, NEW, or PEEK).' ]);
run('BUILD should output a tip when no source is specified',
['build'], [ title, feedMe ]);
run('VALIDATE should output a tip when no source is specified',
['validate'], [ title, feedMe ]);
run('ANALYZE should output a tip when no source is specified',
['analyze'], [ title, feedMe ]);
run('BUILD should display an error on a broken resume',
['build',
'node_modules/fresh-test-resumes/src/fresh/johnny-trouble.broken.json',
'-t', 'modern'
], [ title, 'Error: Invalid or corrupt JSON on line' ]);
run('CONVERT should output a tip when no source is specified',
['convert'], [ title, feedMe ]);
run('NEW should output a tip when no source is specified',
['new'], [ title, 'Please specify the filename of the resume to create.' ]);
// This will cause the HELP doc to be emitted, followed by an "unknown option --help"
// error in the log, based on the way we're calling into HMR. As long as the test
// passes, any extraneous error messages can be ignored here.
run('HMR should output help doc with --help',
['--help'], [ manPage ]);
run('HMR should accept raw JSON via --options',
[
'build',
'node_modules/fresh-test-resumes/src/fresh/jane-fullstacker.json',
'to',
'test/sandbox/temp/janeq-1.all',
'-o',
"{ theme: 'compact', debug: true, pdf: 'wkhtmltopdf' }"],
[ 'Applying COMPACT theme (', '(with wkhtmltopdf)'] );
run('HMR should accept a JSON settings file via --options',
[
'build',
'node_modules/fresh-test-resumes/src/fresh/jane-fullstacker.json',
'to',
'test/sandbox/temp/janeq-2.all',
'--options',
"test/hmr-options.json"],
[ 'Applying POSITIVE theme'] );
run('Explicit command line options should override --options',
[
'build',
'node_modules/fresh-test-resumes/src/fresh/jane-fullstacker.json',
'to',
'test/sandbox/temp/janeq-3.all',
'--options',
"test/hmr-options.json",
"-t",
"modern"
],
[ 'Applying MODERN theme'] );
});

View File

@ -1,67 +0,0 @@
var chai = require('chai')
, expect = chai.expect
, should = chai.should()
, path = require('path')
, _ = require('underscore')
, Sheet = require('../src/core/sheet')
, validator = require('is-my-json-valid');
chai.config.includeStack = false;
describe('fullstack.json', function () {
var _sheet;
it('should open without throwing an exception', function () {
function tryOpen() {
_sheet = new Sheet().open( 'node_modules/resample/fullstack/in/resume.json' );
}
tryOpen.should.not.Throw();
});
it('should have one or more of each section', function() {
expect(
(_sheet.basics) &&
(_sheet.work && _sheet.work.length > 0) &&
(_sheet.skills && _sheet.skills.length > 0) &&
(_sheet.education && _sheet.education.length > 0) &&
(_sheet.volunteer && _sheet.volunteer.length > 0) &&
(_sheet.publications && _sheet.publications.length > 0) &&
(_sheet.awards && _sheet.awards.length > 0)
).to.equal( true );
});
it('should have a work duration of 11 years', function() {
_sheet.computed.numYears.should.equal( 11 );
});
it('should save without throwing an exception', function(){
function trySave() {
_sheet.save( 'tests/sandbox/fullstack.json' );
}
trySave.should.not.Throw();
});
it('should not be modified after saving', function() {
var savedSheet = new Sheet().open( 'tests/sandbox/fullstack.json' );
_sheet.stringify().should.equal( savedSheet.stringify() )
});
it('should validate against the JSON Resume schema', function() {
var schemaJson = require('../src/core/resume.json');
var validate = validator( schemaJson, { verbose: true } );
var result = validate( JSON.parse( _sheet.meta.raw ) );
result || console.log("\n\nOops, resume didn't validate. " +
"Validation errors:\n\n", validate.errors, "\n\n");
result.should.equal( true );
});
});
// describe('subtract', function () {
// it('should return -1 when passed the params (1, 2)', function () {
// expect(math.subtract(1, 2)).to.equal(-1);
// });
// });