mirror of
https://github.com/JuanCanham/HackMyResume.git
synced 2024-12-23 13:47:22 +00:00
Merge remote-tracking branch 'refs/remotes/origin/dev'
This commit is contained in:
commit
5bc8b9c987
58
CONTRIBUTING.md
Normal file
58
CONTRIBUTING.md
Normal 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
|
10
Gruntfile.js
10
Gruntfile.js
@ -14,10 +14,10 @@ module.exports = function (grunt) {
|
||||
ui: 'bdd',
|
||||
reporter: 'spec'
|
||||
},
|
||||
all: { src: ['tests/*.js'] }
|
||||
all: { src: ['test/*.js'] }
|
||||
},
|
||||
|
||||
clean: ['tests/sandbox'],
|
||||
clean: ['test/sandbox'],
|
||||
|
||||
yuidoc: {
|
||||
compile: {
|
||||
@ -38,7 +38,7 @@ module.exports = function (grunt) {
|
||||
laxcomma: true,
|
||||
expr: true
|
||||
},
|
||||
all: ['Gruntfile.js', 'src/**/*.js', 'test/**/*.js']
|
||||
all: ['Gruntfile.js', 'src/**/*.js', 'test/*.js']
|
||||
}
|
||||
|
||||
};
|
||||
@ -51,9 +51,9 @@ module.exports = function (grunt) {
|
||||
grunt.loadNpmTasks('grunt-contrib-clean');
|
||||
|
||||
grunt.registerTask('test', 'Test the HackMyResume library.',
|
||||
function( config ) { grunt.task.run( ['clean','simplemocha:all'] ); });
|
||||
function( config ) { grunt.task.run( ['clean','jshint','simplemocha:all'] ); });
|
||||
grunt.registerTask('document', 'Generate HackMyResume library documentation.',
|
||||
function( config ) { grunt.task.run( ['yuidoc'] ); });
|
||||
grunt.registerTask('default', [ 'jshint', 'test', 'yuidoc' ]);
|
||||
grunt.registerTask('default', [ 'test', 'yuidoc' ]);
|
||||
|
||||
};
|
||||
|
102
README.md
102
README.md
@ -32,6 +32,7 @@ or Windows.
|
||||
- Support for multiple input and output resumes.
|
||||
- Use from your command line or [desktop][7].
|
||||
- Free and open-source through the MIT license.
|
||||
- Updated daily.
|
||||
|
||||
## Install
|
||||
|
||||
@ -42,8 +43,36 @@ Install HackMyResume with NPM:
|
||||
```
|
||||
|
||||
Note: for PDF generation you'll need to install a copy of [wkhtmltopdf][3] for
|
||||
your platform. For LaTeX generation you'll need a valid LaTeX environment with
|
||||
access to `xelatex` and similar.
|
||||
your platform.
|
||||
|
||||
## 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
|
||||
|
||||
@ -98,9 +127,9 @@ 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).
|
||||
MS Word | .doc | A Microsoft Word office document.
|
||||
Adobe Acrobat (PDF) | .pdf | A binary PDF document driven by an HTML theme.
|
||||
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.
|
||||
@ -108,15 +137,6 @@ RTF | .rtf | Forthcoming.
|
||||
Textile | .textile | Forthcoming.
|
||||
image | .png, .bmp | Forthcoming.
|
||||
|
||||
## Install
|
||||
|
||||
HackMyResume requires a recent version of [Node.js][4] and [NPM][5]. Then:
|
||||
|
||||
1. Install the latest official [wkhtmltopdf][3] binary for your platform.
|
||||
2. Optionally install an updated LaTeX environment (LaTeX resumes only).
|
||||
2. Install **HackMyResume** with `[sudo] npm install hackmyresume -g`.
|
||||
3. You're ready to go.
|
||||
|
||||
## Use
|
||||
|
||||
Assuming you've got a JSON-formatted resume handy, generating resumes in
|
||||
@ -132,19 +152,19 @@ theme (default to Modern). For example:
|
||||
|
||||
```bash
|
||||
# Generate all resume formats (HTML, PDF, DOC, TXT, YML, etc.)
|
||||
hackmyresume build resume.json -o out/resume.all -t modern
|
||||
hackmyresume BUILD resume.json TO out/resume.all -t modern
|
||||
|
||||
# Generate a specific resume format
|
||||
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
|
||||
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
|
||||
hackmyresume build in1.json in2.json TO out.html out.doc out.pdf
|
||||
hackmyresume BUILD in1.json in2.json TO out.html out.doc out.pdf
|
||||
```
|
||||
|
||||
You should see something to the effect of:
|
||||
@ -171,8 +191,8 @@ For a predefined theme, include the theme name. For a custom theme, include the
|
||||
path to the custom theme's folder.
|
||||
|
||||
```bash
|
||||
hackmyresume build resume.json -t modern
|
||||
hackmyresume build resume.json -t ~/foo/bar/my-custom-theme/
|
||||
hackmyresume BUILD resume.json TO out/rez.all -t modern
|
||||
hackmyresume BUILD resume.json TO OUT.rez.all -t ~/foo/bar/my-custom-theme/
|
||||
```
|
||||
|
||||
As of v1.0.0, available predefined themes are `positive`, `modern`, `compact`,
|
||||
@ -185,7 +205,7 @@ most generic to most specific:
|
||||
|
||||
```bash
|
||||
# Merge specific.json onto base.json and generate all formats
|
||||
hackmyresume build 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
|
||||
@ -196,7 +216,7 @@ resume. Merging follows conventional [extend()][9]-style behavior and there's
|
||||
no arbitrary limit to how many resumes you can merge:
|
||||
|
||||
```bash
|
||||
hackmyresume build in1.json in2.json in3.json in4.json TO out.html 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
|
||||
@ -212,14 +232,7 @@ You can specify **multiple output targets** and HackMyResume will build them:
|
||||
|
||||
```bash
|
||||
# Generate out1.doc, out1.pdf, and foo.txt from me.json.
|
||||
hackmyresume build 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 "hackmyresume resume.json resume.all -t modern"
|
||||
hackmyresume build resume.json
|
||||
hackmyresume BUILD me.json TO out1.doc out1.pdf foo.txt
|
||||
```
|
||||
|
||||
### Using .all
|
||||
@ -229,7 +242,7 @@ formats for the given resume. For example, this...
|
||||
|
||||
```bash
|
||||
# Generate all resume formats (HTML, PDF, DOC, TXT, etc.)
|
||||
hackmyresume build me.json -o out/resume.all
|
||||
hackmyresume BUILD me.json TO out/resume.all
|
||||
```
|
||||
|
||||
..tells HackMyResume to read `me.json` and generate `out/resume.md`,
|
||||
@ -244,7 +257,7 @@ resumes, use the `validate` command:
|
||||
|
||||
```bash
|
||||
# Validate myresume.json against either the FRESH or JSON Resume schema.
|
||||
hackmyresume validate resumeA.json resumeB.json
|
||||
hackmyresume VALIDATE resumeA.json resumeB.json
|
||||
```
|
||||
|
||||
HackMyResume will validate each specified resume in turn:
|
||||
@ -276,7 +289,7 @@ HTML-formatted resumes. To disable prettification, the `--nopretty` or `-n` flag
|
||||
can be used:
|
||||
|
||||
```bash
|
||||
hackmyresume generate resume.json out.all --nopretty
|
||||
hackmyresume BUILD resume.json out.all --nopretty
|
||||
```
|
||||
|
||||
### Silent Mode
|
||||
@ -284,10 +297,16 @@ hackmyresume generate resume.json out.all --nopretty
|
||||
Use `-s` or `--silent` to run in silent mode:
|
||||
|
||||
```bash
|
||||
hackmyresume generate resume.json -o someFile.all -s
|
||||
hackmyresume generate resume.json -o someFile.all --silent
|
||||
hackmyresume BUILD resume.json -o someFile.all -s
|
||||
hackmyresume BUILD resume.json -o someFile.all --silent
|
||||
```
|
||||
|
||||
## 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.
|
||||
@ -307,3 +326,6 @@ MIT. Go crazy. See [LICENSE.md][1] for details.
|
||||
[dry]: https://en.wikipedia.org/wiki/Don%27t_repeat_yourself
|
||||
[travis-image]: https://img.shields.io/travis/palomajs/paloma.svg?style=flat-square
|
||||
[travis-url]: https://travis-ci.org/hacksalot/HackMyResume
|
||||
[contribute]: CONTRIBUTING.md
|
||||
[fresh-themes]: https://github.com/fluentdesk/fluent-themes
|
||||
[jrst]: https://www.npmjs.com/search?q=jsonresume-theme
|
||||
|
17
package.json
17
package.json
@ -1,13 +1,13 @@
|
||||
{
|
||||
"name": "hackmyresume",
|
||||
"version": "1.2.2",
|
||||
"version": "1.3.0-beta",
|
||||
"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/hacksalot/HackMyResume.git"
|
||||
},
|
||||
"scripts": {
|
||||
"test": "mocha && grunt",
|
||||
"test": "grunt clean && mocha",
|
||||
"grunt": "grunt"
|
||||
},
|
||||
"keywords": [
|
||||
@ -29,6 +29,11 @@
|
||||
"template"
|
||||
],
|
||||
"author": "hacksalot <hacksalot@indevious.com> (https://github.com/hacksalot)",
|
||||
"contributors": [
|
||||
"Edmund Jorgensen (https://github.com/tomheon)",
|
||||
"Ya Zhuang (https://github.com/zhuangya)",
|
||||
"hacksalot <hacksalot@indevious.com> (https://github.com/hacksalot)"
|
||||
],
|
||||
"license": "MIT",
|
||||
"preferGlobal": "true",
|
||||
"bugs": {
|
||||
@ -41,7 +46,8 @@
|
||||
"homepage": "https://github.com/hacksalot/HackMyResume",
|
||||
"dependencies": {
|
||||
"colors": "^1.1.2",
|
||||
"fluent-themes": "~0.7.1-beta",
|
||||
"copy": "^0.1.3",
|
||||
"fluent-themes": "~0.8.0-beta",
|
||||
"fresca": "~0.2.2",
|
||||
"fs-extra": "^0.24.0",
|
||||
"handlebars": "^4.0.5",
|
||||
@ -57,6 +63,7 @@
|
||||
"path-exists": "^2.1.0",
|
||||
"recursive-readdir-sync": "^1.0.6",
|
||||
"simple-html-tokenizer": "^0.2.0",
|
||||
"string.prototype.startswith": "^0.2.0",
|
||||
"underscore": "^1.8.3",
|
||||
"webshot": "^0.16.0",
|
||||
"wkhtmltopdf": "^0.1.5",
|
||||
@ -72,6 +79,10 @@
|
||||
"grunt-contrib-yuidoc": "^0.10.0",
|
||||
"grunt-simple-mocha": "*",
|
||||
"jane-q-fullstacker": "fluentdesk/jane-q-fullstacker",
|
||||
"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"
|
||||
}
|
||||
|
93
src/core/error-handler.js
Normal file
93
src/core/error-handler.js
Normal file
@ -0,0 +1,93 @@
|
||||
/**
|
||||
@module error-handler.js
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
var HACKMYSTATUS = require('./status-codes')
|
||||
, PKG = require('../../package.json')
|
||||
, title = ('\n*** HackMyResume v' + PKG.version + ' ***').bold.white;
|
||||
|
||||
var ErrorHandler = module.exports = {
|
||||
|
||||
|
||||
err: function( ex, shouldExit ) {
|
||||
var msg = '', exitCode;
|
||||
|
||||
if( ex.fluenterror ){
|
||||
switch( ex.fluenterror ) { // TODO: Remove magic numbers
|
||||
|
||||
case HACKMYSTATUS.themeNotFound:
|
||||
msg = "The specified theme couldn't be found: " + ex.data;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.copyCSS:
|
||||
msg = "Couldn't copy CSS file to destination folder";
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.resumeNotFound:
|
||||
msg = 'Please '.guide + 'specify a valid input resume'.guide.bold +
|
||||
' in FRESH or JSON Resume format.'.guide;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.missingCommand:
|
||||
msg = title + "\nPlease ".guide + "specify a command".guide.bold + " (".guide +
|
||||
Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
|
||||
return (idx === ar.length - 1 ? 'or '.guide : '') +
|
||||
v.toUpperCase().guide;
|
||||
}).join(', '.guide) + ").\n\n".guide +
|
||||
FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' ).info.bold;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.invalidCommand:
|
||||
msg = 'Please '.guide + 'specify the output resume file'.guide.bold +
|
||||
' that should be created.'.guide;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.resumeNotFoundAlt:
|
||||
msg = 'Please '.guide + 'specify a valid input resume'.guide.bold +
|
||||
' in either FRESH or JSON Resume format.'.guide;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.inputOutputParity:
|
||||
msg = 'Please '.guide + 'specify an output file name'.guide.bold +
|
||||
' for every input file you wish to convert.'.guide;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.createNameMissing:
|
||||
msg = 'Please '.guide + 'specify the filename of the resume'.guide.bold +
|
||||
' to create.'.guide;
|
||||
break;
|
||||
|
||||
case HACKMYSTATUS.wkhtmltopdf:
|
||||
msg = 'ERROR: PDF generation failed. '.red.bold + ('Make sure wkhtmltopdf is ' +
|
||||
'installed and accessible from your path.').red;
|
||||
break;
|
||||
|
||||
}
|
||||
exitCode = ex.fluenterror;
|
||||
|
||||
}
|
||||
else {
|
||||
msg = ex.toString();
|
||||
exitCode = 4;
|
||||
}
|
||||
|
||||
var idx = msg.indexOf('Error: ');
|
||||
var trimmed = idx === -1 ? msg : msg.substring( idx + 7 );
|
||||
if( !ex.fluenterror || ex.fluenterror < 3 ) { // TODO: magic #s
|
||||
console.log( ('ERROR: ' + trimmed.toString()).red.bold );
|
||||
console.log( ex.stack.gray);
|
||||
}
|
||||
else {
|
||||
console.log( trimmed.toString() );
|
||||
}
|
||||
|
||||
if( shouldExit )
|
||||
process.exit( exitCode );
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}());
|
@ -143,15 +143,12 @@ Definition of the FRESHResume class.
|
||||
};
|
||||
|
||||
/**
|
||||
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 &
|
||||
Initialize the FreshResume from JSON data.
|
||||
Open and parse the specified FRESH resume. 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.
|
||||
*/
|
||||
FreshResume.prototype.parse = function( stringData, opts ) {
|
||||
|
||||
// Parse the incoming JSON representation
|
||||
var rep = JSON.parse( stringData );
|
||||
|
||||
FreshResume.prototype.parseJSON = function( rep, opts ) {
|
||||
// Convert JSON Resume to FRESH if necessary
|
||||
if( rep.basics ) {
|
||||
rep = CONVERTER.toFRESH( rep );
|
||||
@ -178,6 +175,13 @@ Definition of the FRESHResume class.
|
||||
return this;
|
||||
};
|
||||
|
||||
/**
|
||||
Initialize the the FreshResume from string data.
|
||||
*/
|
||||
FreshResume.prototype.parse = function( stringData, opts ) {
|
||||
return this.parseJSON( JSON.parse( stringData ), opts );
|
||||
};
|
||||
|
||||
/**
|
||||
Return a unique list of all keywords across all skills.
|
||||
*/
|
||||
|
@ -11,6 +11,7 @@ Definition of the JRSResume class.
|
||||
, validator = require('is-my-json-valid')
|
||||
, _ = require('underscore')
|
||||
, PATH = require('path')
|
||||
, MD = require('marked')
|
||||
, moment = require('moment');
|
||||
|
||||
/**
|
||||
@ -70,6 +71,7 @@ Definition of the JRSResume class.
|
||||
};
|
||||
|
||||
/**
|
||||
Initialize the JRS Resume from string data.
|
||||
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.
|
||||
@ -77,7 +79,14 @@ Definition of the JRSResume class.
|
||||
JRSResume.prototype.parse = function( stringData, opts ) {
|
||||
opts = opts || { };
|
||||
var rep = JSON.parse( stringData );
|
||||
return this.parseJSON( rep, opts );
|
||||
};
|
||||
|
||||
/**
|
||||
Initialize the JRSRume from JSON data.
|
||||
*/
|
||||
JRSResume.prototype.parseJSON = function( rep, opts ) {
|
||||
opts = opts || { };
|
||||
extend( true, this, rep );
|
||||
// Set up metadata
|
||||
if( opts.imp === undefined || opts.imp ) {
|
||||
@ -230,6 +239,70 @@ Definition of the JRSResume class.
|
||||
|
||||
};
|
||||
|
||||
JRSResume.prototype.dupe = function() {
|
||||
var rnew = new JRSResume();
|
||||
rnew.parse( this.stringify(), { } );
|
||||
return rnew;
|
||||
};
|
||||
|
||||
/**
|
||||
Create a copy of this resume in which all fields have been interpreted as
|
||||
Markdown.
|
||||
*/
|
||||
JRSResume.prototype.harden = function() {
|
||||
|
||||
var that = this;
|
||||
var ret = this.dupe();
|
||||
|
||||
function HD(txt) {
|
||||
return '@@@@~' + txt + '~@@@@';
|
||||
}
|
||||
|
||||
function HDIN(txt){
|
||||
//return MD(txt || '' ).replace(/^\s*<p>|<\/p>\s*$/gi, '');
|
||||
return HD(txt);
|
||||
}
|
||||
|
||||
// TODO: refactor recursion
|
||||
function hardenStringsInObject( obj, inline ) {
|
||||
|
||||
if( !obj ) return;
|
||||
inline = inline === undefined || inline;
|
||||
|
||||
|
||||
if( Object.prototype.toString.call( obj ) === '[object Array]' ) {
|
||||
obj.forEach(function(elem, idx, ar){
|
||||
if( typeof elem === 'string' || elem instanceof String )
|
||||
ar[idx] = inline ? HDIN(elem) : HD( elem );
|
||||
else
|
||||
hardenStringsInObject( elem );
|
||||
});
|
||||
}
|
||||
else if (typeof obj === 'object') {
|
||||
Object.keys( obj ).forEach(function(key) {
|
||||
var sub = obj[key];
|
||||
if( typeof sub === 'string' || sub instanceof String ) {
|
||||
if( _.contains(['skills','url','website','startDate','endDate','releaseDate','date','phone','email','address','postalCode','city','country','region'], key) )
|
||||
return;
|
||||
if( key === 'summary' )
|
||||
obj[key] = HD( obj[key] );
|
||||
else
|
||||
obj[key] = inline ? HDIN( obj[key] ) : HD( obj[key] );
|
||||
}
|
||||
else
|
||||
hardenStringsInObject( sub );
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
Object.keys( ret ).forEach(function(member){
|
||||
hardenStringsInObject( ret[ member ] );
|
||||
});
|
||||
|
||||
return ret;
|
||||
};
|
||||
|
||||
/**
|
||||
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
|
||||
|
@ -1,13 +0,0 @@
|
||||
(function(){
|
||||
|
||||
var FRESHResume = require('../core/fresh-resume');
|
||||
|
||||
module.exports = function loadSourceResumes( src, log, fn ) {
|
||||
return src.map( function( res ) {
|
||||
log( 'Reading '.info + 'SOURCE'.infoBold + ' resume: '.info +
|
||||
res.cyan.bold );
|
||||
return (fn && fn(res)) || (new FRESHResume()).open( res );
|
||||
});
|
||||
};
|
||||
|
||||
}());
|
43
src/core/resume-factory.js
Normal file
43
src/core/resume-factory.js
Normal file
@ -0,0 +1,43 @@
|
||||
/**
|
||||
Core resume-loading logic for HackMyResume.
|
||||
@module resume-factory.js
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
require('string.prototype.startswith');
|
||||
var FS = require('fs');
|
||||
var ResumeConverter = require('./convert');
|
||||
|
||||
/**
|
||||
A simple factory class for FRESH and JSON Resumes.
|
||||
@class ResumeFactory
|
||||
*/
|
||||
module.exports = {
|
||||
|
||||
/**
|
||||
Load one or more resumes in a specific source format.
|
||||
*/
|
||||
load: function ( src, log, fn, toFormat ) {
|
||||
|
||||
toFormat = toFormat && (toFormat.toLowerCase().trim()) || 'fresh';
|
||||
var ResumeClass = require('../core/' + toFormat + '-resume');
|
||||
|
||||
return src.map( function( res ) {
|
||||
var rezJson = JSON.parse( FS.readFileSync( res ) );
|
||||
var orgFormat = ( rezJson.meta && rezJson.meta.format &&
|
||||
rezJson.meta.format.startsWith('FRESH@') ) ?
|
||||
'fresh' : 'jrs';
|
||||
if(orgFormat !== toFormat) {
|
||||
rezJson = ResumeConverter[ 'to' + toFormat.toUpperCase() ]( rezJson );
|
||||
}
|
||||
// TODO: Core should not log
|
||||
log( 'Reading '.info + orgFormat.toUpperCase().infoBold + ' resume: '.info + res.cyan.bold );
|
||||
return (fn && fn(res)) || (new ResumeClass()).parseJSON( rezJson );
|
||||
});
|
||||
|
||||
}
|
||||
|
||||
};
|
||||
|
||||
}());
|
22
src/core/spawn-watch.js
Normal file
22
src/core/spawn-watch.js
Normal file
@ -0,0 +1,22 @@
|
||||
/**
|
||||
@module spawn-watch.js
|
||||
*/
|
||||
|
||||
(function() {
|
||||
|
||||
// Catch various out-of-band child process errors such as ENOENT for PDFs
|
||||
// http://stackoverflow.com/q/27688804
|
||||
var SpawnWatcher = module.exports = function() {
|
||||
var childProcess = require("child_process");
|
||||
var oldSpawn = childProcess.spawn;
|
||||
childProcess.spawn = function() {
|
||||
return oldSpawn.apply(this, arguments)
|
||||
.on('error', function(err) {
|
||||
require('./error-handler').err( err, false );
|
||||
});
|
||||
};
|
||||
}();
|
||||
|
||||
//SpawnWatcher();
|
||||
|
||||
}());
|
21
src/core/status-codes.js
Normal file
21
src/core/status-codes.js
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
Status codes for HackMyResume.
|
||||
@module status-codes.js
|
||||
*/
|
||||
|
||||
(function(){
|
||||
|
||||
module.exports = {
|
||||
success: 0,
|
||||
themeNotFound: 1,
|
||||
copyCss: 2,
|
||||
resumeNotFound: 3,
|
||||
missingCommand: 4,
|
||||
invalidCommand: 5,
|
||||
resumeNotFoundAlt: 6,
|
||||
inputOutputParity: 7,
|
||||
createNameMissing: 8,
|
||||
wkhtmltopdf: 9
|
||||
};
|
||||
|
||||
}());
|
@ -1,6 +1,6 @@
|
||||
/**
|
||||
Definition of the Theme class.
|
||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
||||
@license MIT. Copyright (c) 2015 hacksalot / FluentDesk.
|
||||
@module theme.js
|
||||
*/
|
||||
|
||||
@ -12,6 +12,7 @@ Definition of the Theme class.
|
||||
, _ = require('underscore')
|
||||
, PATH = require('path')
|
||||
, parsePath = require('parse-filepath')
|
||||
, pathExists = require('path-exists').sync
|
||||
, EXTEND = require('../utils/extend')
|
||||
, moment = require('moment')
|
||||
, RECURSIVE_READ_DIR = require('recursive-readdir-sync');
|
||||
@ -29,9 +30,26 @@ Definition of the Theme class.
|
||||
*/
|
||||
Theme.prototype.open = function( themeFolder ) {
|
||||
|
||||
// Open the [theme-name].json file; should have the same name as folder
|
||||
this.folder = themeFolder;
|
||||
|
||||
// Open the [theme-name].json file; should have the same name as folder
|
||||
var pathInfo = parsePath( themeFolder );
|
||||
|
||||
// Set up a formats hash for the theme
|
||||
var formatsHash = { };
|
||||
|
||||
// See if the theme has a package.json. If so, load it.
|
||||
var packageJsonPath = PATH.join(themeFolder, 'package.json');
|
||||
if( pathExists( packageJsonPath ) ) {
|
||||
var themePack = require( themeFolder );
|
||||
var themePkgJson = require( packageJsonPath );
|
||||
this.name = themePkgJson.name;
|
||||
this.render = (themePack && themePack.render) || undefined;
|
||||
this.formats = { html: { title: 'html', outFormat: 'html', ext: 'html', path: null, data: null } };
|
||||
return this;
|
||||
}
|
||||
|
||||
// Otherwise, do a full theme load
|
||||
var themeFile = PATH.join( themeFolder, pathInfo.basename + '.json' );
|
||||
var themeInfo = JSON.parse( FS.readFileSync( themeFile, 'utf8' ) );
|
||||
var that = this;
|
||||
@ -39,9 +57,6 @@ Definition of the Theme class.
|
||||
// Move properties from the theme JSON file to the theme object
|
||||
EXTEND( true, this, themeInfo );
|
||||
|
||||
// Set up a formats has for the theme
|
||||
var formatsHash = { };
|
||||
|
||||
// Check for an explicit "formats" entry in the theme JSON. If it has one,
|
||||
// then this theme declares its files explicitly.
|
||||
if( !!this.formats ) {
|
||||
|
@ -26,14 +26,7 @@ Definition of the BaseGenerator class.
|
||||
/**
|
||||
Status codes.
|
||||
*/
|
||||
codes: {
|
||||
success: 0,
|
||||
themeNotFound: 1,
|
||||
copyCss: 2,
|
||||
resumeNotFound: 3,
|
||||
missingCommand: 4,
|
||||
invalidCommand: 5
|
||||
},
|
||||
codes: require('../core/status-codes'),
|
||||
|
||||
/**
|
||||
Generator options.
|
||||
|
@ -1,6 +1,5 @@
|
||||
/**
|
||||
Definition of the HtmlPdfGenerator class.
|
||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
||||
@module html-pdf-generator.js
|
||||
*/
|
||||
|
||||
@ -23,7 +22,7 @@ Definition of the HtmlPdfGenerator class.
|
||||
Generate the binary PDF.
|
||||
*/
|
||||
onBeforeSave: function( info ) {
|
||||
pdf( info.mk, info.outputFile );
|
||||
pdf.call( this, info.mk, info.outputFile );
|
||||
return null; // halt further processing
|
||||
}
|
||||
|
||||
@ -34,39 +33,50 @@ Definition of the HtmlPdfGenerator class.
|
||||
*/
|
||||
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 } } );
|
||||
});
|
||||
pdf_wkhtmltopdf.call( this, markup, fOut );
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
Generate a PDF from HTML using wkhtmltopdf.
|
||||
*/
|
||||
function pdf_wkhtmltopdf( markup, fOut ) {
|
||||
var wk;
|
||||
try {
|
||||
wk = require('wkhtmltopdf');
|
||||
wk( markup, { pageSize: 'letter' } )
|
||||
.pipe( FS.createWriteStream( fOut ) );
|
||||
}
|
||||
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++;
|
||||
catch(ex) {
|
||||
// { [Error: write EPIPE] code: 'EPIPE', errno: 'EPIPE', syscall: 'write' }
|
||||
// { [Error: ENOENT] }
|
||||
throw { fluenterror: this.codes.wkhtmltopdf };
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
// function pdf_phantom() {
|
||||
// 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 } } );
|
||||
// });
|
||||
// }
|
||||
|
||||
}());
|
||||
|
@ -156,7 +156,7 @@ Definition of the TemplateGenerator class.
|
||||
{ outputFile: fileName, mk: file.data } );
|
||||
}
|
||||
catch(ex) {
|
||||
console.log(ex);
|
||||
require('../core/error-handler').err(ex, false);
|
||||
}
|
||||
}
|
||||
else if( file.info.action === null/* && theme.explicit*/ ) {
|
||||
|
55
src/index.js
55
src/index.js
@ -2,27 +2,32 @@
|
||||
|
||||
/**
|
||||
Command-line interface (CLI) for HackMyResume.
|
||||
@license MIT. Copyright (c) 2015 James M. Devlin / FluentDesk.
|
||||
@license MIT. Copyright (c) 2015 hacksalot (https://github.com/hacksalot)
|
||||
@module index.js
|
||||
*/
|
||||
|
||||
var ARGS = require( 'minimist' )
|
||||
|
||||
|
||||
var SPAWNW = require('./core/spawn-watch')
|
||||
, ARGS = require( 'minimist' )
|
||||
, FCMD = require( './hackmycmd')
|
||||
, PKG = require('../package.json')
|
||||
, COLORS = require('colors')
|
||||
, FS = require('fs')
|
||||
, PATH = require('path')
|
||||
, HACKMYSTATUS = require('./core/status-codes')
|
||||
, opts = { }
|
||||
, title = ('\n*** HackMyResume v' + PKG.version + ' ***').bold.white
|
||||
, _ = require('underscore');
|
||||
|
||||
|
||||
|
||||
|
||||
try {
|
||||
main();
|
||||
}
|
||||
catch( ex ) {
|
||||
handleError( ex );
|
||||
require('./core/error-handler').err( ex, true );
|
||||
}
|
||||
|
||||
|
||||
@ -90,47 +95,3 @@ function getOpts( args ) {
|
||||
silent: args.s || args.silent
|
||||
};
|
||||
}
|
||||
|
||||
// TODO: refactor
|
||||
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 '.guide + 'specify a valid input resume'.guide.bold + ' in FRESH or JSON Resume format.'.guide; break;
|
||||
case 4: msg = title + "\nPlease ".guide + "specify a command".guide.bold + " (".guide +
|
||||
Object.keys( FCMD.verbs ).map( function(v, idx, ar) {
|
||||
return (idx === ar.length - 1 ? 'or '.guide : '') +
|
||||
v.toUpperCase().guide;
|
||||
}).join(', '.guide) + ").\n\n".guide + FS.readFileSync( PATH.join(__dirname, 'use.txt'), 'utf8' ).info.bold;
|
||||
break;
|
||||
//case 4: msg = title + '\n' + ; break;
|
||||
case 5: msg = 'Please '.guide + 'specify the output resume file'.guide.bold + ' that should be created.'.guide; break;
|
||||
case 6: msg = 'Please '.guide + 'specify a valid input resume'.guide.bold + ' in either FRESH or JSON Resume format.'.guide; break;
|
||||
case 7: msg = 'Please '.guide + 'specify an output file name'.guide.bold + ' for every input file you wish to convert.'.guide; break;
|
||||
case 8: msg = 'Please '.guide + 'specify the filename of the resume'.guide.bold + ' to create.'.guide; break;
|
||||
}
|
||||
exitCode = ex.fluenterror;
|
||||
|
||||
}
|
||||
else {
|
||||
msg = ex.toString();
|
||||
exitCode = 4;
|
||||
}
|
||||
|
||||
var idx = msg.indexOf('Error: ');
|
||||
var trimmed = idx === -1 ? msg : msg.substring( idx + 7 );
|
||||
if( !ex.fluenterror || ex.fluenterror < 3 ) { // TODO: magic #s
|
||||
console.log( ('ERROR: ' + trimmed.toString()).red.bold );
|
||||
console.log( ex.stack.gray);
|
||||
}
|
||||
else
|
||||
console.log( trimmed.toString() );
|
||||
|
||||
process.exit( exitCode );
|
||||
|
||||
}
|
||||
|
@ -1,6 +1,5 @@
|
||||
/**
|
||||
Definitions of string utility functions.
|
||||
@license MIT. Copyright (c) 2015 James Devlin / FluentDesk.
|
||||
@module string.js
|
||||
*/
|
||||
|
||||
@ -10,14 +9,18 @@ See: http://stackoverflow.com/a/32800728/4942583
|
||||
@method isNullOrWhitespace
|
||||
*/
|
||||
|
||||
String.isNullOrWhitespace = function( input ) {
|
||||
return !input || !input.trim();
|
||||
};
|
||||
(function() {
|
||||
|
||||
String.prototype.endsWith = function(suffix) {
|
||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||
};
|
||||
String.isNullOrWhitespace = function( input ) {
|
||||
return !input || !input.trim();
|
||||
};
|
||||
|
||||
String.is = function( val ) {
|
||||
return typeof val === 'string' || val instanceof String;
|
||||
};
|
||||
String.prototype.endsWith = function(suffix) {
|
||||
return this.indexOf(suffix, this.length - suffix.length) !== -1;
|
||||
};
|
||||
|
||||
String.is = function( val ) {
|
||||
return typeof val === 'string' || val instanceof String;
|
||||
};
|
||||
|
||||
}());
|
||||
|
@ -1,6 +1,6 @@
|
||||
(function(){
|
||||
|
||||
var loadSourceResumes = require('../core/load-source-resumes');
|
||||
var ResumeFactory = require('../core/resume-factory');
|
||||
|
||||
/**
|
||||
Convert between FRESH and JRS formats.
|
||||
@ -16,7 +16,7 @@
|
||||
if( src && dst && src.length && dst.length && src.length !== dst.length ) {
|
||||
throw { fluenterror: 7 };
|
||||
}
|
||||
var sheets = loadSourceResumes( src, _log );
|
||||
var sheets = ResumeFactory.load( src, _log );
|
||||
sheets.forEach(function(sheet, idx){
|
||||
var sourceFormat = sheet.imp.orgFormat === 'JRS' ? 'JRS' : 'FRESH';
|
||||
var targetFormat = sourceFormat === 'JRS' ? 'FRESH' : 'JRS';
|
||||
|
@ -1,11 +1,13 @@
|
||||
(function() {
|
||||
|
||||
var PATH = require('path')
|
||||
, FS = require('fs')
|
||||
, parsePath = require('parse-filepath')
|
||||
, MD = require('marked')
|
||||
, MKDIRP = require('mkdirp')
|
||||
, _opts = require('../core/default-options')
|
||||
, FluentTheme = require('../core/theme')
|
||||
, loadSourceResumes = require('../core/load-source-resumes')
|
||||
, ResumeFactory = require('../core/resume-factory')
|
||||
, _ = require('underscore')
|
||||
, _fmts = require('../core/default-formats')
|
||||
, _err, _log, rez;
|
||||
@ -36,9 +38,28 @@
|
||||
_opts.theme = (opts.theme && opts.theme.toLowerCase().trim())|| 'modern';
|
||||
_opts.prettify = opts.prettify === true ? _opts.prettify : false;
|
||||
|
||||
// Verify the specified theme name/path
|
||||
var relativeThemeFolder = '../../node_modules/fluent-themes/themes';
|
||||
var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme);
|
||||
var exists = require('path-exists').sync;
|
||||
if( !exists( tFolder ) ) {
|
||||
tFolder = PATH.resolve( _opts.theme );
|
||||
if (!exists( tFolder )) {
|
||||
throw { fluenterror: 1, data: _opts.theme };
|
||||
}
|
||||
}
|
||||
|
||||
// Load the theme
|
||||
var theTheme = (new FluentTheme()).open( tFolder );
|
||||
_opts.themeObj = theTheme;
|
||||
var numFormats = theTheme.formats ? Object.keys(theTheme.formats).length : 2;
|
||||
_log( 'Applying '.info + theTheme.name.toUpperCase().infoBold +
|
||||
(' theme (' + numFormats + ' formats)').info);
|
||||
|
||||
// Load input resumes...
|
||||
if( !src || !src.length ) { throw { fluenterror: 3 }; }
|
||||
var sheets = loadSourceResumes( src, _log );
|
||||
var sheets = ResumeFactory.load( src, _log, null,
|
||||
theTheme.render ? 'JRS' : 'FRESH' );
|
||||
|
||||
// Merge input resumes...
|
||||
var msg = '';
|
||||
@ -49,23 +70,6 @@
|
||||
});
|
||||
msg && _log(msg);
|
||||
|
||||
// Verify the specified theme name/path
|
||||
var relativeThemeFolder = '../../node_modules/fluent-themes/themes';
|
||||
var tFolder = PATH.resolve( __dirname, relativeThemeFolder, _opts.theme);
|
||||
var exists = require('path-exists').sync;
|
||||
if (!exists( tFolder )) {
|
||||
tFolder = PATH.resolve( _opts.theme );
|
||||
if (!exists( tFolder )) {
|
||||
throw { fluenterror: 1, data: _opts.theme };
|
||||
}
|
||||
}
|
||||
|
||||
// Load the theme
|
||||
var theTheme = (new FluentTheme()).open( tFolder );
|
||||
_opts.themeObj = theTheme;
|
||||
_log( 'Applying '.info + theTheme.name.toUpperCase().infoBold +
|
||||
(' theme (' + Object.keys(theTheme.formats).length + ' formats)').info);
|
||||
|
||||
// Expand output resumes... (can't use map() here)
|
||||
var targets = [], that = this;
|
||||
( (dst && dst.length && dst) || ['resume.all'] ).forEach( function(t) {
|
||||
@ -74,11 +78,15 @@
|
||||
pa = parsePath(to),
|
||||
fmat = pa.extname || '.all';
|
||||
|
||||
targets.push.apply(targets, fmat === '.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.outFormat), fmt: z };
|
||||
}) : [{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
|
||||
}) :
|
||||
|
||||
[{ file: to, fmt: theTheme.getFormat( fmat.slice(1) ) }]);
|
||||
|
||||
});
|
||||
|
||||
@ -95,6 +103,11 @@
|
||||
"/foo/bar/resume.pdf" or "c:\foo\bar\resume.txt".
|
||||
*/
|
||||
function single( targInfo, theme ) {
|
||||
|
||||
function MDIN(txt) {
|
||||
return MD(txt || '' ).replace(/^\s*<p>|<\/p>\s*$/gi, '');
|
||||
}
|
||||
|
||||
try {
|
||||
var f = targInfo.file
|
||||
, fType = targInfo.fmt.outFormat
|
||||
@ -113,22 +126,6 @@
|
||||
function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0];
|
||||
MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists;
|
||||
theFormat.gen.generate( rez, f, _opts );
|
||||
|
||||
// targInfo.fmt.files.forEach( function( form ) {
|
||||
//
|
||||
// if( form.action === 'transform' ) {
|
||||
// var theFormat = _fmts.filter( function( fmt ) {
|
||||
// return fmt.name === targInfo.fmt.outFormat;
|
||||
// })[0];
|
||||
// MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists;
|
||||
// theFormat.gen.generate( rez, f, _opts );
|
||||
// }
|
||||
// else if( form.action === null ) {
|
||||
// // Copy the file
|
||||
// }
|
||||
//
|
||||
// });
|
||||
|
||||
}
|
||||
// Otherwise the theme has no files section
|
||||
else {
|
||||
@ -138,8 +135,44 @@
|
||||
|
||||
theFormat = _fmts.filter(
|
||||
function(fmt) { return fmt.name === targInfo.fmt.outFormat; })[0];
|
||||
MKDIRP.sync( PATH.dirname( f ) ); // Ensure dest folder exists;
|
||||
theFormat.gen.generate( rez, f, _opts );
|
||||
|
||||
var outFolder = PATH.dirname( f );
|
||||
MKDIRP.sync( outFolder ); // Ensure dest folder exists;
|
||||
|
||||
// TODO: refactor
|
||||
if( theme.render ) {
|
||||
var COPY = require('copy');
|
||||
var globs = [ /*'**',*/ '*.css', '*.js', '*.png', '*.jpg', '*.gif', '*.bmp' ];
|
||||
COPY.sync( globs , outFolder, {
|
||||
cwd: theme.folder, nodir: true,
|
||||
ignore: ['node_modules/','node_modules/**']
|
||||
// rewrite: function(p1, p2) {
|
||||
// return PATH.join(p2, p1);
|
||||
// }
|
||||
});
|
||||
|
||||
// Prevent JSON Resume theme .js from chattering
|
||||
var consoleLog = console.log;
|
||||
console.log = function() { };
|
||||
|
||||
// Call the theme's render method
|
||||
var rezDupe = rez.harden();
|
||||
var rezHtml = theme.render( rezDupe );
|
||||
|
||||
// Turn logging back on
|
||||
console.log = consoleLog;
|
||||
|
||||
// Unharden
|
||||
rezHtml = rezHtml.replace( /@@@@~.+?~@@@@/g, function(val){
|
||||
return MDIN( val.replace( /~@@@@/gm,'' ).replace( /@@@@~/gm,'' ) );
|
||||
});
|
||||
|
||||
// Save the file
|
||||
FS.writeFileSync( f, rezHtml );
|
||||
}
|
||||
else {
|
||||
theFormat.gen.generate( rez, f, _opts );
|
||||
}
|
||||
}
|
||||
}
|
||||
catch( ex ) {
|
||||
|
@ -1,7 +1,7 @@
|
||||
(function() {
|
||||
|
||||
var FS = require('fs');
|
||||
var loadSourceResumes = require('../core/load-source-resumes');
|
||||
var ResumeFactory = require('../core/resume-factory');
|
||||
|
||||
module.exports =
|
||||
|
||||
@ -20,7 +20,7 @@
|
||||
};
|
||||
|
||||
// Load input resumes...
|
||||
var sheets = loadSourceResumes(src, _log, function( res ) {
|
||||
var sheets = ResumeFactory.load(src, _log, function( res ) {
|
||||
try {
|
||||
return {
|
||||
file: res,
|
||||
|
@ -43,15 +43,15 @@ describe('Testing CLI interface', function () {
|
||||
silent: true
|
||||
};
|
||||
|
||||
run( 'new', ['tests/sandbox/new-fresh-resume.json'], [], opts, ' (FRESH format)' );
|
||||
run( 'new', ['tests/sandbox/new-jrs-resume.json'], [], opts2, ' (JRS format)' );
|
||||
run( 'new', ['tests/sandbox/new-1.json', 'tests/sandbox/new-2.json', 'tests/sandbox/new-3.json'], [], opts, ' (multiple FRESH resumes)' );
|
||||
run( 'new', ['tests/sandbox/new-jrs-1.json', 'tests/sandbox/new-jrs-2.json', 'tests/sandbox/new-jrs-3.json'], [], opts, ' (multiple JRS resumes)' );
|
||||
run( 'new', ['tests/sandbox/new-jrs-resume.json'], [], opts2, ' (JRS format)' );
|
||||
run( 'new', ['test/sandbox/new-fresh-resume.json'], [], opts, ' (FRESH format)' );
|
||||
run( 'new', ['test/sandbox/new-jrs-resume.json'], [], opts2, ' (JRS format)' );
|
||||
run( 'new', ['test/sandbox/new-1.json', 'test/sandbox/new-2.json', 'test/sandbox/new-3.json'], [], opts, ' (multiple FRESH resumes)' );
|
||||
run( 'new', ['test/sandbox/new-jrs-1.json', 'test/sandbox/new-jrs-2.json', 'test/sandbox/new-jrs-3.json'], [], opts, ' (multiple JRS resumes)' );
|
||||
run( 'new', ['test/sandbox/new-jrs-resume.json'], [], opts2, ' (JRS format)' );
|
||||
fail( 'new', [], [], opts, " (when a filename isn't specified)" );
|
||||
|
||||
run( 'validate', ['node_modules/jane-q-fullstacker/resume/jane-resume.json'], [], opts, ' (FRESH format)' );
|
||||
run( 'validate', ['tests/sandbox/new-fresh-resume.json'], [], opts, ' (FRESH format)' );
|
||||
run( 'validate', ['test/sandbox/new-fresh-resume.json'], [], opts, ' (FRESH format)' );
|
||||
|
||||
function run( verb, src, dst, opts, msg ) {
|
||||
msg = msg || '.';
|
||||
|
@ -43,13 +43,13 @@ describe('jane-doe.json (FRESH)', function () {
|
||||
|
||||
it('should save without throwing an exception', function(){
|
||||
function trySave() {
|
||||
_sheet.save( 'tests/sandbox/jane-q-fullstacker.json' );
|
||||
_sheet.save( 'test/sandbox/jane-q-fullstacker.json' );
|
||||
}
|
||||
trySave.should.not.Throw();
|
||||
});
|
||||
|
||||
it('should not be modified after saving', function() {
|
||||
var savedSheet = new FRESHResume().open('tests/sandbox/jane-q-fullstacker.json');
|
||||
var savedSheet = new FRESHResume().open('test/sandbox/jane-q-fullstacker.json');
|
||||
_sheet.stringify().should.equal( savedSheet.stringify() );
|
||||
});
|
||||
|
||||
|
@ -38,13 +38,13 @@ function testResume( opts ) {
|
||||
it('should save without throwing an exception', function() {
|
||||
var that = this;
|
||||
function trySave() {
|
||||
_sheet.save( 'tests/sandbox/' + opts.title + '.json' );
|
||||
_sheet.save( 'test/sandbox/' + opts.title + '.json' );
|
||||
}
|
||||
trySave.should.not.Throw();
|
||||
});
|
||||
|
||||
it('should not be modified after saving', function() {
|
||||
var savedSheet = new JRSResume().open( 'tests/sandbox/' + opts.title + '.json' );
|
||||
var savedSheet = new JRSResume().open( 'test/sandbox/' + opts.title + '.json' );
|
||||
_sheet.stringify().should.equal( savedSheet.stringify() );
|
||||
});
|
||||
|
||||
|
@ -1,5 +1,6 @@
|
||||
|
||||
var chai = require('chai')
|
||||
var SPAWNWATCHER = require('../src/core/spawn-watch')
|
||||
, chai = require('chai')
|
||||
, expect = chai.expect
|
||||
, should = chai.should()
|
||||
, path = require('path')
|
||||
@ -26,28 +27,47 @@ describe('Testing themes', function () {
|
||||
useful: 'green',
|
||||
});
|
||||
|
||||
function genTheme( themeName ) {
|
||||
it( themeName.toUpperCase() + ' theme should generate without throwing an exception', function () {
|
||||
function genTheme( fmt, src, themeName, themeLoc, testTitle ) {
|
||||
themeLoc = themeLoc || themeName;
|
||||
testTitle = themeName.toUpperCase() + ' theme should generate without throwing an exception';
|
||||
it( testTitle, function () {
|
||||
function tryOpen() {
|
||||
var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
|
||||
var dst = ['tests/sandbox/' + themeName + '/resume.all'];
|
||||
//var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
|
||||
var dst = ['test/sandbox/' + fmt + '/' + themeName + '/resume.all'];
|
||||
var opts = {
|
||||
theme: themeName,
|
||||
format: 'FRESH',
|
||||
theme: themeLoc,
|
||||
format: fmt,
|
||||
prettify: true,
|
||||
silent: true
|
||||
};
|
||||
FCMD.verbs.build( src, dst, opts, function() { } );
|
||||
FCMD.verbs.build( src, dst, opts, function() {} );
|
||||
}
|
||||
tryOpen.should.not.Throw();
|
||||
});
|
||||
}
|
||||
|
||||
genTheme('hello-world');
|
||||
genTheme('compact');
|
||||
genTheme('modern');
|
||||
genTheme('minimist');
|
||||
genTheme('awesome');
|
||||
genTheme('positive');
|
||||
var src = ['node_modules/jane-q-fullstacker/resume/jane-resume.json'];
|
||||
genTheme('FRESH', src, 'hello-world');
|
||||
genTheme('FRESH', src, 'compact');
|
||||
genTheme('FRESH', src, 'modern');
|
||||
genTheme('FRESH', src, 'minimist');
|
||||
genTheme('FRESH', src, 'awesome');
|
||||
genTheme('FRESH', src, 'positive');
|
||||
genTheme('FRESH', src, 'jsonresume-theme-boilerplate', 'node_modules/jsonresume-theme-boilerplate' );
|
||||
genTheme('FRESH', src, 'jsonresume-theme-sceptile', 'node_modules/jsonresume-theme-sceptile' );
|
||||
genTheme('FRESH', src, 'jsonresume-theme-modern', 'node_modules/jsonresume-theme-modern' );
|
||||
genTheme('FRESH', src, 'jsonresume-theme-classy', 'node_modules/jsonresume-theme-classy' );
|
||||
|
||||
src = ['test/resumes/jrs-0.0.0/richard-hendriks.json'];
|
||||
genTheme('JRS', src, 'hello-world');
|
||||
genTheme('JRS', src, 'compact');
|
||||
genTheme('JRS', src, 'modern');
|
||||
genTheme('JRS', src, 'minimist');
|
||||
genTheme('JRS', src, 'awesome');
|
||||
genTheme('JRS', src, 'positive');
|
||||
genTheme('JRS', src, 'jsonresume-theme-boilerplate', 'node_modules/jsonresume-theme-boilerplate' );
|
||||
genTheme('JRS', src, 'jsonresume-theme-sceptile', 'node_modules/jsonresume-theme-sceptile' );
|
||||
genTheme('JRS', src, 'jsonresume-theme-modern', 'node_modules/jsonresume-theme-modern' );
|
||||
genTheme('JRS', src, 'jsonresume-theme-classy', 'node_modules/jsonresume-theme-classy' );
|
||||
|
||||
});
|
||||
|
Loading…
Reference in New Issue
Block a user