├── .eslintrc.js
├── .github
└── workflows
│ └── npm-publish-github-packages.yml
├── .gitignore
├── .npmrc
├── .prettierrc.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── _config.yml
├── bin
└── gloria
├── build
└── .gitkeep
├── docs
├── commands
│ ├── collect.md
│ └── copy.md
├── index.draft.md
├── index.md
└── tests.html
├── layouts
└── tailwind
│ ├── blog.layout.md
│ ├── index.layout.md
│ ├── index.page.draft.md
│ └── main.css
├── lib
├── bin
│ ├── commands
│ │ ├── build.js
│ │ ├── collect.js
│ │ ├── copy.js
│ │ ├── css.tailwind.js
│ │ ├── extract.js
│ │ ├── prebuild.js
│ │ ├── setup.js
│ │ └── write.js
│ └── gloria.js
├── build
│ ├── handlebars-helpers.js
│ └── render.js
├── commands
│ ├── build.js
│ ├── collect.js
│ ├── copy.js
│ ├── css.tailwind.js
│ ├── extract.js
│ ├── init.js
│ ├── migrate.js
│ ├── new.js
│ ├── new
│ │ ├── create-content.js
│ │ └── meta.js
│ ├── prebuild.js
│ ├── serve.js
│ ├── setup.js
│ └── write.html.js
├── core
│ ├── project.config.js
│ └── project.js
├── utils
│ ├── compilers.js
│ ├── configstore.js
│ ├── filters.js
│ ├── fs.js
│ ├── handlebars.js
│ ├── logger.js
│ └── meta.js
└── version.js
├── logs
└── .gitkeep
├── package.json
├── public
├── CNAME
└── logo.png
├── test
├── commands
│ ├── build.spec.js
│ ├── index.spec.js
│ ├── init.spec.js
│ ├── new.spec.js
│ └── serve.spec.js
├── test.spec.js
└── utils
│ └── logger.spec.js
├── tsconfig.json
└── yarn.lock
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | parser: '@typescript-eslint/parser',
3 | plugins: ['@typescript-eslint'],
4 | extends: [
5 | 'plugin:@typescript-eslint/recommended',
6 | 'plugin:prettier/recommended',
7 | ],
8 | };
9 |
--------------------------------------------------------------------------------
/.github/workflows/npm-publish-github-packages.yml:
--------------------------------------------------------------------------------
1 | # This workflow will run tests using node and then publish a package to GitHub Packages when a release is created
2 | # For more information see: https://docs.github.com/en/actions/publishing-packages/publishing-nodejs-packages
3 |
4 | name: Node.js Package
5 |
6 | on:
7 | release:
8 | types: [created]
9 |
10 | jobs:
11 | build:
12 | runs-on: ubuntu-latest
13 | steps:
14 | - uses: actions/checkout@v3
15 | - uses: actions/setup-node@v3
16 | with:
17 | node-version: 16
18 | - run: yarn install
19 | - run: npm test
20 |
21 | publish-gpr:
22 | needs: build
23 | runs-on: ubuntu-latest
24 | permissions:
25 | contents: read
26 | packages: write
27 | steps:
28 | - uses: actions/checkout@v3
29 | - uses: actions/setup-node@v3
30 | with:
31 | node-version: 16
32 | registry-url: https://npm.pkg.github.com/
33 | - run: yarn install
34 | - run: npm publish
35 | env:
36 | NODE_AUTH_TOKEN: ${{secrets.GITHUB_TOKEN}}
37 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | .DS_Store
2 |
3 | # Logs
4 | logs
5 | *.log
6 | npm-debug.log*
7 |
8 | # Runtime data
9 | pids
10 | *.pid
11 | *.seed
12 | *.pid.lock
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # node-waf configuration
27 | .lock-wscript
28 |
29 | # Compiled binary addons (http://nodejs.org/api/addons.html)
30 | build/Release
31 |
32 | # Dependency directories
33 | node_modules
34 | jspm_packages
35 |
36 | # Optional npm cache directory
37 | .npm
38 |
39 | # Optional eslint cache
40 | .eslintcache
41 |
42 | # Optional REPL history
43 | .node_repl_history
44 |
45 | # Output of 'npm pack'
46 | *.tgz
47 |
48 | # Yarn Integrity file
49 | .yarn-integrity
50 |
51 | # files that are generated by the tests
52 | sample
53 |
54 | # VSCode metadata
55 | .vscode
56 |
57 | #Jetbrains metadata
58 | .idea/
59 |
60 | # Folder to ignore for contributors
61 | _notes
62 | node_modules
63 | build-logs/*
64 | build/
65 | cli.js
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | @gloriajs:registry=https://npm.pkg.github.com
--------------------------------------------------------------------------------
/.prettierrc.json:
--------------------------------------------------------------------------------
1 | {
2 | "trailingComma": "all",
3 | "singleQuote": true
4 | }
5 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # NEXTVERSION
2 |
3 | # 2.2.3
4 | - Stopped collecting files to be copied to avoid certain bugs
5 |
6 | # 2.2.2
7 | - Added basic handlebars filters for easy navigation
8 |
9 | # 2.2.1
10 | - Creating setup command to generate default _config.yml
11 |
12 | # 2.2.0-a
13 | - Fixing copy command that was corrupting images
14 |
15 | # 2.1.8
16 | - Wrote github action to publish github package
17 |
18 | # 2.1.7
19 | - Write command created
20 |
21 | # 2.1.6
22 | - Copying files that are meant to be copied before running CSS
23 | - Fixed sample files
24 |
25 | # 2.1.5
26 | - Tailwind command
27 | - Writes to file abstracted
28 |
29 | # 2.1.4
30 | - Build command correctly writes temp files to disk
31 |
32 | # 2.1.3
33 | - Build command works with md and handlebars
34 | - Partial typescript support
35 | - Added the prebuild commandd
36 | - Added the build command
37 |
38 | # 2.1.2
39 |
40 | - Added the extract command
41 |
42 | # 2.1.1
43 |
44 | - Added the copy command
45 |
46 | # 2.1
47 |
48 | - Rewriting commands to be more modular
49 |
50 | # 2
51 |
52 | - Dropped support for sass
53 |
54 | # 0.18.0
55 |
56 | - added a meta tag for responsive in default template
57 | - using UTC for moment when parsing dates
58 |
59 | # 0.17.0
60 |
61 | - Reveresed 0.16.3
62 |
63 | # 0.16.3
64 |
65 | - Fixed error where pages data was duplicated
66 |
67 | # 0.16.2
68 |
69 | - Fixed error with including data and pages to for content
70 |
71 | # 0.16.1
72 |
73 | - Added direction to each with sort
74 |
75 | # 0.16.0
76 |
77 | - Adds a timestamp property to pages data
78 | - Created a method eachWithSort for a handlebar helper
79 |
80 | # 0.15.1
81 |
82 | - Added formatDate helper to handlebars helpers
83 |
84 | # 0.15.0
85 |
86 | - adding filename to the data available for page
87 | - Use frontmatter to customize layouts and includes (Issue #13)
88 |
89 | # 0.14.0
90 |
91 | - Not clearing destination folder by default when serving
92 |
93 | #0.13.0
94 |
95 | - Reading files from the theme folder and including them in the build process
96 | - Allowing users to have different themes, and include the correct files from the theme selected
97 | - Fixed bug occuring to windows users when building/serving #97
98 |
99 | #0.12.0
100 |
101 | - Using an utils file for logger
102 | - Using config store to save configuration information for the module
103 | - Suggesting users to download the latest version if available
104 |
105 | #0.11.0
106 |
107 | - Removed unused dependencies
108 | - Added code of conduct
109 | - Replaced jscs with eslint
110 |
111 | #0.10.0
112 |
113 | - Fixed bug that prevent init from working on windows
114 | - Added build stats
115 | - Watches files and builds during serve
116 | - Allows to add additional data as json files, in the \_data folder.
117 | The data can be used in the templates as {{data.'jsonFileName'}}
118 |
119 | #0.9.6
120 |
121 | - Fix issue #59, every post has a correct url, even when it has categories
122 |
123 | #0.9.5
124 |
125 | - Performance improvements in the build command
126 | - Fixed bugs with prompts
127 | - Increased test coverage
128 | - Allows to chose a layout from the provided ones
129 | - Added support for \_stylus files
130 | - Added a `watch` flag for serve command
131 |
132 | #0.9.4
133 |
134 | - Added the `new` command to make it easier adding new content
135 |
136 | #0.9.3
137 |
138 | - Refactored the render file, for the build command
139 |
140 | #0.9.2
141 |
142 | - Added performance fixes
143 |
144 | #0.9.1
145 | #0.9.0
146 |
147 | ## Added the option to include posts
148 |
149 | - Posts can be included in a folder \_posts, they will be rendered using the
150 | layout `post.html`.
151 |
152 | - There's a property named posts, that can be used to get a lists of posts
153 | to lists them.
154 |
155 | - The bootstrap layout, includes examples on how to use them.
156 |
157 | **@todo**: - Prevent errors when templates don't exists. - Create helpers to allow nesting layout/template - Create new command
158 |
159 | #0.8.3
160 |
161 | - Fixed typo in package.json
162 |
163 | #0.8.2
164 |
165 | - Replaced the markdown engine with [marked](https://github.com/chjj/marked).
166 |
167 | #0.8.1
168 |
169 | - Simple bug fixes
170 |
171 | #0.8.0
172 | **Simple support for sass**
173 |
174 | - Using node-sass, it will look for main.scss files in a \_sass folder.
175 | It will then output the compiled files into {dest}/sass/
176 |
177 | **@todo**: allow to customize the source and destination of sass files.
178 |
179 | #0.7.0
180 | **Added a migrate command**
181 |
182 | - Allows to migrate a jekyll site to gloria. Poorly,
183 | as described [here](https://github.com/gloriajs/gloria/issues/15).
184 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 | Our open source community is a group of people committed to developing awesome software that Works.
2 | Every other concern is subordinate to this goal.
3 | We recognize that there are certain kinds of childish behavior that are unwelcome in our community.
4 | We respond to such behavior by generally ignoring and/or—in extreme cases—making sport of, individuals who engage in such behavior.
5 | We accept everyone's contributions, we don't care about your personal life! In fact, we won't bring it up, or ask.
6 | Those conversations have no place in the repo, issues or community spaces unless it refers to accesbility, inclusion or other important features of the tool.
7 | Repeat offenders will get mocked and banned.
8 | If you need to complain about someone's behavior or report something we suggest to message the maintainers.
9 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # CONTRIBUTING
2 |
3 | Contributions in the form of ideas, designs, and code are always welcome no matter how large or small.
4 | If you want to help but are not sure where to start, you can check out our project's
5 | [Issues](https://github.com/gloriajs/gloria/issues).
6 |
7 | For contributing instructions look at this page:
8 | [gloria.js.org/contributing/](https://gloria.js.org//contributing/).
9 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Permission is hereby granted, free of charge, to any person obtaining a copy of
2 | this software and associated documentation files (the "Software"), to deal in
3 | the Software with specific restrictions FOR the purposes of satire, evil, or
4 | advancing evil, including but not limited to:
5 |
6 | Horatian, Juvenalian, Menippean, Irony, Hyperbole, Understatement, Allegory,
7 | bringing down the American Empire and ending the War on Drugs.
8 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # Gloria - static site generator
2 |
3 | _gloria is spanish for glory, also the name of my mom and the name was available in npm_
4 |
5 | **V2 is deprecating lots of old functions, make sure to read the migrating docs.** We're also slowly moving into typescript, and while I think it should work very well for your projects, legally I need to remind you that this is a satire of other projects.
6 |
7 | It currently supports having a content management system using MD files and tailwind CSS, with some bugs, but the main functionality and philosophy have been accomplished.
8 |
9 | This project aims to be a substitute for [jekyll](https://jekyllrb.com/), to help you create static websites without depending on ruby.
10 |
11 | Because of its ease of use, extensibility and support for md files, is also great for creating websites out of documentatation that lives in a source code.
12 |
13 | ## Community
14 |
15 | We're going to have a discord or something some day.
16 |
17 | ## Quickstart
18 |
19 | Gloria aims to be the easiest simple static web generator there is, get yourself a good looking website for your portfolio, projects, community or hobbies in less than 5 minutes.
20 |
21 | ### Web
22 |
23 | Fork the quick start theme repo and modify `_config.yml`, add and change pages, and publish to your [github pages](https://pages.github.com/) for free.
24 |
25 | ### Locally
26 |
27 | Add gloria as a dependency `yarn add gloriajs` and follow build and deploy instructions. We're using a `yarn.lock`.
28 |
29 | ### Custom domains
30 |
31 | Github pages offers support for custom domains, follow the instructions on [their docs](https://docs.github.com/en/pages/configuring-a-custom-domain-for-your-github-pages-site/managing-a-custom-domain-for-your-github-pages-site).
32 |
33 | [JS.org](https://js.org/) Offers free domains to open source
34 |
35 | ## Installation
36 |
37 | For more information check our [website](https://gloria.js.org).
38 |
39 | ### Npm Registry
40 |
41 | To install from the npm registry run `yarn add gloriajs` or `npm install gloriajs`.
42 |
43 | ### Github packages
44 |
45 | Add the `@gloriajs` scope to your `.npmrc` or your project before installing. If added globally it only needs to be done once, but is better to add it on each project so your collaborators don't have to do it too before getting started. I can't remember my npm login, but once I figure it out we can also publish there I guess.
46 |
47 | ```
48 | @gloriajs:registry=npm.pkg.github.com
49 | ```
50 |
51 | Install by
52 |
53 | ```
54 | npm i @gloriajs/gloria
55 | ```
56 |
57 | ## Usage
58 |
59 | Now gloria is globally installed in your computer, and you can run commands like `gloria --version` to retrieve the version. Or if installed per project you can use npx.
60 |
61 | ### Locally
62 |
63 | Clone this repo and point to it from another one adding this line to your dependencies:
64 |
65 | ```
66 | "gloria-local": "./node_modules/gloria-local/bin/gloria"
67 | ```
68 |
69 | Then you can add scripts to your `package.json` as:
70 |
71 | ```
72 | "gloria-local": "./node_modules/gloria-local/bin/gloria"
73 | ```
74 |
75 | And use `gloria` from that folder like:
76 |
77 | ```
78 | npm run gloria-local -- --version
79 | ```
80 |
81 | ## Commands
82 |
83 | - `collect` - `[output]` traverses the include-paths and saves information about the available files
84 | - `copy` - `[path] [dest]` copies the static files from source paths to dest folders
85 | - `extract` - `[dest]` finds metadata and frontmatter information from every collected file
86 | - `prebuild` - `[dest]` creates the output placeholders so they can be processed
87 | - `build` - `[path] [dest]` interpolates the content into the layout
88 | - `css:tailwind` - `[path] [dest]` runs tailwind in the html output
89 | - `scripts` - `[path] [dest]` @TODO processes scripts
90 | - `write` - `[dest]` writes production version to disk
91 | - `cleanup` - `[dest]` @TODO: cleans up temp files after a successful build
92 | - `version` - ` ` returns the current package version
93 |
94 | The commands all should work on their own, and the ones that combine multiple steps do it very nicely by using promises, we pass the project around, and store and read from its properties, to create new commands or amplify existing ones, for plugins and templates, review the data structure.
95 |
96 | ## Development and contributing
97 |
98 | Refer to our [Contributing page](CONTRIBUTING.md) and local installation instructions.
99 |
100 | **Here's our @TODO in no particular order for inspiration:**
101 |
102 | - [x] Write the previous commands to restore basic features
103 | - [x] Fix build so it interpolates the parsed markdown
104 | - [x] Recreate documentation website
105 | - [ ] Move to typescript
106 | - [ ] Delete unused files
107 | - [ ] Data sources
108 | - [x] interpolations
109 | - [ ] bug: only excluding first condition
110 |
111 | ## Data structure
112 |
113 | The current data structure is something like:
114 |
115 | ```
116 | const project = {
117 | prebuilt: {
118 | html,
119 | css,
120 | },
121 | };
122 | ```
123 |
124 | Once we're in typescript it will be much easier to see by referencing the interfaces.
125 |
126 | ## CSS
127 |
128 | I'm using tailwind coz it seemed easy, should be quite extendable and possible to include other css pre-processors, or change the configuration flag.
129 |
130 | ## Tests
131 |
132 | I removed this line `./node_modules/.bin/mocha --reporter spec` and need to add it later, but now that we're using TS and new testing tools have come out, perhaps we don't need mocha.
133 |
134 | ## Troubleshooting
135 |
136 | Try on your own or open an [issue](https://github.com/gloriajs/gloria/issues) describing your problem.
137 |
138 | Go for a walk and wait.
139 |
140 | ## google analytics
141 |
142 | I recommend not tracking vanity metrics when you could be going for a walk, but if you must, add it to the layout or wait for a way to append_to_head using metadata from a page.
143 |
144 | ## Slava Ukraini
145 |
146 | Gloria and Slava are the same word. A friend named Slava told me.
147 |
--------------------------------------------------------------------------------
/_config.yml:
--------------------------------------------------------------------------------
1 | # sample configuration
2 | # is also used when testing commands on this folder
3 | name: Gloria JS
4 | tagline: 'A simple static website generator'
5 | baseurl: 'https://gloriajs.github.io'
6 | author: 'daveed silva'
7 | description: 'Documentation for the tool gloriajs'
8 | dest: build
9 | theme:
10 | - layouts/tailwind
11 | css: tailwind
12 | include:
13 | - docs
14 | exclude:
15 | - '.draft.md'
16 | copy:
17 | - public
18 |
--------------------------------------------------------------------------------
/bin/gloria:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env node
2 | require('../lib/bin/gloria');
3 |
--------------------------------------------------------------------------------
/build/.gitkeep:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gloriajs/gloria/be7d9cbce14c36a191047f4cc0a0622a6b86e19a/build/.gitkeep
--------------------------------------------------------------------------------
/docs/commands/collect.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Collect command
3 | description: Documentation website for the collect command GloriaJS
4 | permalink: docs/command/collect
5 | layout: blog
6 | ---
7 |
8 | # Command
9 |
10 | ```
11 | ./bin/gloria collect --dest=logs
12 | ```
13 |
14 | Debugging command.
15 |
16 | Runs the collect command and writes the output to the console, or to a file.
17 |
18 | Useful for debuging or profiling.
19 |
20 | Sample output:
21 |
22 | ```
23 | {"include":[],"copy":[],"styles":[]}
24 | ```
25 |
--------------------------------------------------------------------------------
/docs/commands/copy.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Copy command
3 | description: Documentation website for the copy command GloriaJS
4 | permalink: docs/command/copy
5 | layout: blog
6 | ---
7 |
8 | # Command
9 |
10 | ```
11 | ./bin/gloria copy --dest=build --path=public
12 | ```
13 |
14 | Command to copy the static assets into destination folder.
15 |
--------------------------------------------------------------------------------
/docs/index.draft.md:
--------------------------------------------------------------------------------
1 | This post will be ignored by the collector
2 |
--------------------------------------------------------------------------------
/docs/index.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: GloriaJS
3 | description: Documentation for gloriajs
4 | permalink: docs/
5 | ---
6 |
7 | # Welcome to GloriaJS
8 |
9 | The following commands are available and exist:
10 |
11 | - [x] collect all files
12 | - [x] collect assets
13 | - [x] pre-process content pages
14 | - [x] process content data and layouts
15 | - [x] process tailwind
16 | - [x] write to file system
17 |
18 | We might open a discord later.
19 |
20 | This project hopes to make simple websites super simple to create and deploy, specially for super easy things you don't wanna worry about, that way everyone can focus on their amazing content without worrying about obscure compile errors.
21 |
22 | A configuration file looks like this:
23 |
24 | ```
25 | name: Gloria JS
26 | tagline: 'A simple static website generator'
27 | baseurl: 'https://gloriajs.github.io'
28 | author: 'daveed silva'
29 | description: 'Documentation for the tool gloriajs'
30 | dest: build
31 | theme:
32 | - layouts/tailwind
33 | css: tailwind
34 | include:
35 | - docs
36 | exclude:
37 | - '.draft.md'
38 | copy:
39 | - public
40 | ```
41 |
42 | You can add any additional properties here and they will be available for you in the templates as properties of project.config
.
43 |
44 | ## Commands
45 |
46 | The following commands are available to use
47 |
48 |
49 | - `collect` - `[output]` traverses the include-paths and saves information about the available files
50 | - `copy` - `[path] [dest]` copies the static files from source paths to dest folders
51 | - `extract` - `[dest]` finds metadata and frontmatter information from every collected file
52 | - `prebuild` - `[dest]` creates the output placeholders so they can be processed
53 | - `build` - `[path] [dest]` interpolates the content into the layout
54 | - `css:tailwind` - `[path] [dest]` runs tailwind in the html output
55 | - `scripts` - `[path] [dest]` @TODO processes scripts
56 | - `write` - `[dest]` @TODO writes production version to disk and cleans temp files
57 | - `version` - ` ` returns the current package version
58 |
59 | The commands all should work on their own, and the ones that combine multiple steps do it very nicely by using promises, we pass the project around, and store and read from its properties, to create new commands or amplify existing ones, for plugins and templates, review the data structure.
60 |
61 | Find the website documentation in [github](https://github.com/gloriajs/gloriajs.github.io)
62 |
63 | Or the live website in [gloria.js.org](https://gloria.js.org/)
64 |
65 |
Template
66 |
67 | Templates are html or markdown files, they are interpreted with handlebars .
68 |
69 | There are three main objects you can access on your template, site
has access to the properties specified
70 | in _config.yml
.
71 | page
has access to the properties specified in the header of the page, and args
has
72 | access to the arguments given to the command build.
73 |
74 | Layouts
75 |
76 | A layout file is used to have a structure common to different pages, for example a navigation menu, header, css includes,
77 | scripts, etc.
78 |
79 | The name of the layout will be the name of the file, by default default.html
is included, additional
80 | files included in the _layout
folder will be used. If no layout exists or is especified the file
81 | will be rendered on its own.
82 |
83 | The main thing that templates require is {{{page.content}}}
that will get replaced with the actual content
84 | of the page.
85 |
86 | Includes
87 |
88 | Include files, or also known as partials, are files that can be included in your content or layouts, they can be
89 | useful to reuse snippets like a head
element, or a share button.
90 |
91 | Any amount of partials and includes can exists, in the _includes
folder, they are named as their file
92 | name, and they can be used like this {{> head}}
. Data will be interpolated there as expected.
93 |
94 | Frontmatter
95 |
96 | Every page can have additional attributes that will be accessible to it, the layout or the includes.
97 |
98 | The additional attributes should be included in the beginning of the file in the following format, use three dashes
99 | in the first line of the file, and add any key pair of attributes using the
100 | Yaml syntax, and finish with three dashes. Like this:
101 |
`
102 |
103 | This change allows accessing variables defined in layouts' and includes' frontmatters. This could be achieved through the following syntax. {{ page.g.layout.var1}} for accessing layout variables or
104 |
105 | {{ page.g.head.var1}} for accessing includes' (hbs partials) variables. head here is the name of the include. g (short for gloria) acts as a namespace to avoid collision between common variable names like 'title' or 'description'.
106 |
107 |
108 |
109 |
110 |
111 |
112 |
113 | ---
114 |
115 | title: About us
116 | url: /about
117 | description:
118 |
119 | ---
120 |
121 |
122 |
123 |
124 |
125 |
126 | Issues
127 |
128 | There's no layouts or including other files yet.
129 |
130 | Roadmap, also includes having access to other pages, to be able to loop in blog posts, or read categories for example.
131 |
132 | Deployment
133 |
134 | You can use any service that lets you host HTML or static sites. The quickest ones to get working are github pages and
135 | firebase .
136 |
137 | Firebase instructions
138 |
139 | Create a firebase account and a project, then run firebase init
on your root folder and follow the instructions.
140 | When prompted what folder to use, chose your destination folder.
141 |
142 | Github pages instructions
143 |
144 | The best option to use github pages is to build your site to a folder called docs
, push to a repo, and
145 | then select that folder in the github-pages configuration for that repo.
146 |
147 | Contributing
148 |
149 | We are creating a contributing guide, find more information
150 | here .
151 |
152 |
153 |
154 | Troubleshooting
155 |
156 | I'm currently using:
157 |
158 |
159 |
160 | node v6.4.0
161 | npm 3.10.3
162 |
163 |
164 | Try upgrading your version of node and run yarn
again. Or open an issue describing your problem.
165 |
166 |
167 |
168 |
--------------------------------------------------------------------------------
/docs/tests.html:
--------------------------------------------------------------------------------
1 | ---
2 | title: GloriaJS
3 | description: Documentation for testing with gloriaJS
4 | permalink: tests/
5 | layout: blog
6 | ---
7 | Tests
8 | Tests are not only possible, but desirable.
--------------------------------------------------------------------------------
/layouts/tailwind/blog.layout.md:
--------------------------------------------------------------------------------
1 | ---
2 | layout: index
3 | name: blog
4 | title: Blog
5 | ---
6 |
7 |
8 |
9 |
10 | {{site.title}}
11 |
12 |
13 |
16 |
17 |
18 |
19 |
20 |
{{page.title}}
21 |
22 | {{{page.content}}}
23 |
24 |
25 |
26 |
27 |
28 |
--------------------------------------------------------------------------------
/layouts/tailwind/index.layout.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Welcome!
3 | description: Meow
4 | permalink: ''
5 | name: index
6 | ---
7 |
8 |
9 |
10 |
11 | {{page.title}}
12 |
13 |
14 |
17 |
18 |
19 |
20 |
21 | {{{content}}}
22 |
23 |
24 |
25 |
26 |
--------------------------------------------------------------------------------
/layouts/tailwind/index.page.draft.md:
--------------------------------------------------------------------------------
1 | ---
2 | title: Welcome draft
3 | description:
4 | ---
5 |
6 | Skip this page during prebuild.
7 |
--------------------------------------------------------------------------------
/layouts/tailwind/main.css:
--------------------------------------------------------------------------------
1 | @tailwind base;
2 | @tailwind components;
3 | @tailwind utilities;
--------------------------------------------------------------------------------
/lib/bin/commands/build.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _collect = require('../../commands/collect');
4 | const _extract = require('../../commands/extract');
5 | const _prebuild = require('../../commands/prebuild');
6 | const _build = require('../../commands/build');
7 | const _write_html = require('../../commands/write.html');
8 | const fs = require('fs');
9 |
10 | /**
11 | * work in progress, always add on top of existing ways and prevent
12 | * backwards compatibility issues
13 | *
14 | * Default include-paths are pages and posts.
15 | *
16 | * This method
17 | * @param {string} argv.dest If present, write result to destination file
18 | * @param {string} argv.folder If present writes files to folder
19 | * @param {string} argv.write If false, skips writing files to disk
20 | *
21 | * @return {object} information like content and medatata from every coollected path
22 | */
23 | const build = (argv) => {
24 | const project = {
25 | config: config.get(),
26 | collected: {},
27 | content: {},
28 | extracted: {},
29 | prebuilt: {
30 | files: {},
31 | styles: {},
32 | scripts: {},
33 | },
34 | html: [],
35 | };
36 |
37 | _collect
38 | .run(project)
39 | .then((project) => _extract.run(project))
40 | .then((project) => _prebuild.run(project))
41 | .then((project) => _build.run(project))
42 | .then((prochecto) => {
43 | const { config } = prochecto;
44 |
45 | if (argv.dest) {
46 | const dest = `./${argv.dest}/built.${config._hash}.json`;
47 | const ztring = JSON.stringify(prochecto);
48 | fs.writeFileSync(dest, ztring);
49 | }
50 |
51 | // logger.log({ ...prochecto });
52 | return _write_html.run(prochecto);
53 | })
54 | .catch((e) => {
55 | logger.error({ e });
56 | });
57 | };
58 |
59 | const options = {
60 | dest: {
61 | default: null,
62 | description: `Destination folder to output JSON report.`,
63 | },
64 | };
65 |
66 | /**
67 | * Command to compile html files
68 | */
69 | module.exports = {
70 | command: `build [dest]`,
71 | aliases: ['html'],
72 | describe: `Compiles files, interpolates data and writes temp files to chosen destination.
73 | By default it will use a folder name 'build' in the root directory of the project,
74 | Check your _config.yml to change.
75 | It won't build to a parent folder.
76 | The command will fail if _config.yml is invalid or not present.`,
77 | builder: options,
78 | handler: build,
79 | };
80 |
--------------------------------------------------------------------------------
/lib/bin/commands/collect.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _collect = require('../../commands/collect');
4 | const fs = require('fs');
5 |
6 | /**
7 | * Normally called as part of another command, but it can be used
8 | * directly for debugging purposes.
9 | * When given an output argument it will write the results and
10 | * additional information to disk in a json file.
11 | *
12 | * Default include-paths are pages and posts.
13 | * @param {string} argv.dest If present, write result to destination file
14 | */
15 | const collect = (argv) => {
16 | const project = {
17 | config: config.get(),
18 | };
19 |
20 | _collect.run(project).then((project) => {
21 | const { config } = project;
22 |
23 | if (argv.dest) {
24 | const dest = `./${argv.dest}/collected.${config._hash}.json`;
25 | const ztring = JSON.stringify(project.collected);
26 | fs.writeFileSync(dest, ztring);
27 | }
28 |
29 | // logger.log({ collected: project.collected });
30 |
31 | return project;
32 | });
33 | };
34 |
35 | const options = {
36 | dest: {
37 | default: null,
38 | description: `Destination path or folder to output a json file with results.`,
39 | },
40 | };
41 |
42 | module.exports = {
43 | command: `collect [output]`,
44 | aliases: [],
45 | describe: `Collects the files and data that will be processed.`,
46 | builder: options,
47 | handler: collect,
48 | };
49 |
--------------------------------------------------------------------------------
/lib/bin/commands/copy.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _copy = require('../../commands/copy');
4 | const fs = require('node:fs');
5 | const path = require('path');
6 | const root = process.cwd();
7 |
8 | /**
9 | * Normally called as part of another command, but it can be used
10 | * directly for debugging purposes or faster asset collection.
11 | *
12 | * @param {string} argv.dest If present, write result to destination folder
13 | * @param {string} argv.path If present, override the configuration
14 | */
15 | const copy = (argv) => {
16 | const project = {
17 | config: config.get(),
18 | };
19 |
20 | if (argv.dest) {
21 | project.config.dest = argv.dest;
22 | }
23 |
24 | _copy.run(project).then((project) => {
25 | const { config } = project;
26 |
27 | config.copy.map((p) => {
28 | const sourceDir = path.join(root, p);
29 | const destinationDir = path.join(root, config.dest);
30 |
31 | if (!fs.existsSync(destinationDir)) {
32 | fs.mkdirSync(destinationDir, { recursive: true });
33 | }
34 |
35 | fs.cp(sourceDir, destinationDir, { recursive: true }, function (error) {
36 | if (error) {
37 | throw error;
38 | }
39 | });
40 | });
41 |
42 | return project;
43 | });
44 | };
45 |
46 | const options = {
47 | dest: {
48 | default: null,
49 | description: `Destination folder`,
50 | },
51 | path: {
52 | default: null,
53 | description: 'Ignore the path on configuration and use this folder instead',
54 | },
55 | };
56 |
57 | module.exports = {
58 | command: `copy [dest] [paths]`,
59 | aliases: [],
60 | describe: `Copy the asset files into the output folder.`,
61 | builder: options,
62 | handler: copy,
63 | };
64 |
--------------------------------------------------------------------------------
/lib/bin/commands/css.tailwind.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _collect = require('../../commands/collect');
4 | const _extract = require('../../commands/extract');
5 | const _prebuild = require('../../commands/prebuild');
6 | const _build = require('../../commands/build');
7 | const _css_tailwind = require('../../commands/css.tailwind');
8 | const fs = require('fs');
9 | const upath = require('upath');
10 | const _write_html = require('../../commands/write.html');
11 |
12 | /**
13 | * work in progress, always add on top of existing ways and prevent
14 | * backwards compatibility issues
15 | *
16 | * Default include-paths are pages and posts.
17 | *
18 | * This method
19 | * @param {string} argv.dest If present, write result to destination file
20 | * @param {string} argv.folder If present writes files to folder
21 | * @param {string} argv.write If false, skips writing files to disk
22 | *
23 | * @return {object} information like content and medatata from every coollected path
24 | */
25 | const build = (argv) => {
26 | const project = {
27 | config: config.get(),
28 | collected: {},
29 | content: {},
30 | extracted: {},
31 | prebuilt: {
32 | files: {},
33 | styles: {},
34 | scripts: {},
35 | },
36 | html: [],
37 | };
38 |
39 | _collect
40 | .run(project)
41 | .then((project) => _extract.run(project))
42 | .then((project) => _prebuild.run(project))
43 | .then((project) => _build.run(project))
44 | .then((prochecto) => {
45 | const { config, html } = prochecto;
46 |
47 | if (argv.dest) {
48 | const dest = `./${argv.dest}/built.${config._hash}.json`;
49 | const ztring = JSON.stringify(prochecto);
50 | fs.writeFileSync(dest, ztring);
51 | }
52 |
53 | return _write_html.run(prochecto);
54 | })
55 | .then((project) => _css_tailwind.run(project))
56 | .then((project) => {
57 | // logger.log({ ...project });
58 | })
59 | .catch((e) => {
60 | logger.error({ e });
61 | });
62 | };
63 |
64 | const options = {
65 | dest: {
66 | default: null,
67 | description: `Destination for stylesheet.`,
68 | },
69 | };
70 |
71 | /**
72 | * Command to compile html files
73 | */
74 | module.exports = {
75 | command: `css:tailwind [dest]`,
76 | aliases: ['tailwind'],
77 | describe: `Reads the html output and creates a tailwind stylesheet.
78 | The command will fail if _config.yml is invalid or not present.`,
79 | builder: options,
80 | handler: build,
81 | };
82 |
--------------------------------------------------------------------------------
/lib/bin/commands/extract.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _collect = require('../../commands/collect');
4 | const _extract = require('../../commands/extract');
5 | const fs = require('fs');
6 |
7 | /**
8 | * work in progress, always add on top of existing ways and prevent
9 | * backwards compatibility issues
10 | *
11 | * Default include-paths are pages and posts.
12 | *
13 | * This method
14 | * @param {string} argv.dest If present, write result to destination file
15 | * @param {string} argv.path If present overrides configuration file
16 | *
17 | * @return {object} information like content and medatata from every coollected path
18 | */
19 | const extract = (argv) => {
20 | const project = {
21 | config: config.get(),
22 | collected: {},
23 | content: {},
24 | extracted: {},
25 | };
26 |
27 | _collect
28 | .run(project)
29 | .then(_extract.run)
30 | .then((p) => {
31 | const { config } = p;
32 |
33 | if (argv.dest) {
34 | const dest = `./${argv.dest}/extracted.${config._hash}.json`;
35 | const ztring = JSON.stringify(p.extracted);
36 | fs.writeFileSync(dest, ztring);
37 | }
38 |
39 | // logger.log({ ...p, ...p.extracted });
40 | return p;
41 | })
42 | .catch((e) => {
43 | logger.error({ e });
44 | });
45 | };
46 |
47 | const options = {
48 | dest: {
49 | default: null,
50 | description: `Destination folder to output a json file with results.`,
51 | },
52 | };
53 |
54 | /**
55 | * Command to read md files and other data and create taxonomy
56 | */
57 | module.exports = {
58 | command: `extract [dest] [path]`,
59 | aliases: [],
60 | describe: `Traverses the source files and extracts metadata.`,
61 | builder: options,
62 | handler: extract,
63 | };
64 |
--------------------------------------------------------------------------------
/lib/bin/commands/prebuild.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _collect = require('../../commands/collect');
4 | const _extract = require('../../commands/extract');
5 | const _prebuild = require('../../commands/prebuild');
6 | const fs = require('fs');
7 |
8 | /**
9 | * work in progress, always add on top of existing ways and prevent
10 | * backwards compatibility issues
11 | *
12 | * Default include-paths are pages and posts.
13 | *
14 | * This method
15 | * @param {string} argv.dest If present, write result to destination file
16 | * @param {string} argv.path If present overrides configuration file
17 | *
18 | * @return {object} information like content and medatata from every coollected path
19 | */
20 | const prebuild = (argv) => {
21 | const project = {
22 | config: config.get(),
23 | collected: {},
24 | content: {},
25 | extracted: {},
26 | prebuilt: {
27 | files: {},
28 | styles: {},
29 | scripts: {},
30 | },
31 | };
32 |
33 | _collect
34 | .run(project)
35 | .then(_extract.run)
36 | .then(_prebuild.run)
37 | .then((prochecto) => {
38 | const { config } = prochecto;
39 |
40 | if (argv.dest) {
41 | const dest = `./${argv.dest}/extracted.${config._hash}.json`;
42 | const ztring = JSON.stringify(prochecto.extracted);
43 | fs.writeFileSync(dest, ztring);
44 | }
45 |
46 | return prochecto;
47 | })
48 | .catch((e) => {
49 | logger.error({ e });
50 | });
51 | };
52 |
53 | const options = {
54 | dest: {
55 | default: null,
56 | description: `Destination folder to output a json file with results.`,
57 | },
58 | };
59 |
60 | /**
61 | * Command to prepare everything for compilation
62 | */
63 | module.exports = {
64 | command: `prebuild [dest] [path]`,
65 | aliases: [],
66 | describe: `Gets all files ready for compilation, applying the metadata extracted.`,
67 | builder: options,
68 | handler: prebuild,
69 | };
70 |
--------------------------------------------------------------------------------
/lib/bin/commands/setup.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _setup = require('../../commands/setup');
4 | const fs = require('fs');
5 |
6 | /**
7 | * Normally called as part of another command, but it can be used
8 | * directly for debugging purposes.
9 | * When given an output argument it will write the results and
10 | * additional information to disk in a json file.
11 | *
12 | * Default include-paths are pages and posts.
13 | * @param {string} argv.dest If present, write result to destination file
14 | */
15 | const setup = (argv) => {
16 | const { defaults } = config;
17 |
18 | _setup.run({ config: defaults }, argv.overwrite).then((project) => {
19 | // logger.log();
20 |
21 | return project;
22 | });
23 | };
24 |
25 | const options = {
26 | overwrite: {
27 | default: false,
28 | description: `Overwrite existing files: not yet supported.`,
29 | },
30 | };
31 |
32 | module.exports = {
33 | command: `setup`,
34 | aliases: [],
35 | describe: `Attemps to create a _config.yml file and the necesary folders.`,
36 | builder: options,
37 | handler: setup,
38 | };
39 |
--------------------------------------------------------------------------------
/lib/bin/commands/write.js:
--------------------------------------------------------------------------------
1 | const logger = require('../../utils/logger');
2 | const config = require('../../core/project.config');
3 | const _collect = require('../../commands/collect');
4 | const _extract = require('../../commands/extract');
5 | const _prebuild = require('../../commands/prebuild');
6 | const _build = require('../../commands/build');
7 | const _write_html = require('../../commands/write.html');
8 | const fs = require('fs');
9 |
10 | /**
11 | * work in progress, always add on top of existing ways and prevent
12 | * backwards compatibility issues
13 | *
14 | * Default include-paths are pages and posts.
15 | *
16 | * This method
17 | * @param {string} argv.dest If present, write result to destination file
18 | * @param {string} argv.folder If present writes files to folder
19 | * @param {string} argv.write If false, skips writing files to disk
20 | *
21 | * @return {object} information like content and medatata from every coollected path
22 | */
23 | const write = (argv) => {
24 | const project = {
25 | config: config.get(),
26 | collected: {},
27 | content: {},
28 | extracted: {},
29 | prebuilt: {
30 | files: {},
31 | styles: {},
32 | scripts: {},
33 | },
34 | html: [],
35 | };
36 |
37 | _collect
38 | .run(project)
39 | .then((project) => _extract.run(project))
40 | .then((project) => _prebuild.run(project))
41 | .then((project) => _build.run(project))
42 | .then((prochecto) => {
43 | const { config } = prochecto;
44 |
45 | if (argv.dest) {
46 | const dest = `./${argv.dest}/built.${config._hash}.json`;
47 | const ztring = JSON.stringify(prochecto);
48 | fs.writeFileSync(dest, ztring);
49 | }
50 |
51 | // logger.log({ ...prochecto });
52 | return _write_html.run(prochecto);
53 | })
54 | .catch((e) => {
55 | logger.error({ e });
56 | });
57 | };
58 |
59 | const options = {
60 | dest: {
61 | default: null,
62 | description: `Destination folder.`,
63 | },
64 | };
65 |
66 | /**
67 | * Command to compile html files
68 | */
69 | module.exports = {
70 | command: `write [dest]`,
71 | aliases: ['write:html'],
72 | describe: `Compiles files, interpolates data and writes files to their correct url.
73 | By default it will use a folder name 'build' in the root directory of the project,
74 | Check your _config.yml to change.
75 | It won't build to a parent folder.
76 | The command will fail if _config.yml is invalid or not present.`,
77 | builder: options,
78 | handler: write,
79 | };
80 |
--------------------------------------------------------------------------------
/lib/bin/gloria.js:
--------------------------------------------------------------------------------
1 | process.bin = process.title = `gloria`;
2 | const version = require('../version');
3 | const yargs = require(`yargs`);
4 |
5 | /**
6 | * @name init
7 | * @description uses yargs to enable the commands available
8 | */
9 | const init = () => {
10 | yargs
11 | .commandDir(`./commands`, {
12 | extensions: ['js', 'ts'],
13 | })
14 | .alias('v', 'version')
15 | .version(version)
16 | .help()
17 | .usage(
18 | `Gloria is a static site generator. Use the command init to get started.`,
19 | )
20 | .epilogue(
21 | `For more information, check out the documentation in github.com/gloriajs/gloria`,
22 | ).argv;
23 | };
24 |
25 | init();
26 |
--------------------------------------------------------------------------------
/lib/build/handlebars-helpers.js:
--------------------------------------------------------------------------------
1 | const $log = require('../utils/logger');
2 | const Handlebars = require('handlebars');
3 | const fse = require('fs-extra');
4 | const moment = require('moment');
5 |
6 | Handlebars.registerHelper('replace', (options) => '');
7 | Handlebars.registerHelper('noop', (options) => options.fn(this));
8 | Handlebars.registerHelper('for', (options) => $log.log(options));
9 |
10 | Handlebars.registerHelper('capitalize', function (value) {
11 | return value && typeof value === 'string'
12 | ? value.replace(/\b\w/g, (l) => l.toUpperCase())
13 | : '';
14 | });
15 |
16 | Handlebars.registerHelper('eachWithSort', (array, key, direction, opts) => {
17 | let data;
18 | if (opts.data) {
19 | data = Handlebars.createFrame(opts.data);
20 | }
21 |
22 | const sorted = array.sort((a, b) => {
23 | a = a[key];
24 | b = b[key];
25 | if (direction === '-') {
26 | return a < b ? 1 : a == b ? 0 : -1;
27 | } else {
28 | return a > b ? 1 : a == b ? 0 : -1;
29 | }
30 | });
31 |
32 | let s = '';
33 | sorted.forEach((e, i) => {
34 | if (data) {
35 | data.index = i;
36 | }
37 |
38 | s += opts.fn(e, data);
39 | });
40 |
41 | return s;
42 | });
43 |
44 | Handlebars.registerHelper('formatDate', function (value, format) {
45 | format = format || 'MMM Do YY';
46 | return moment.utc(value).format(format);
47 | });
48 |
49 | Handlebars.registerHelper('ifeq', function (a, b, options) {
50 | if (a === b) {
51 | return options.fn(this);
52 | }
53 |
54 | return options.inverse(this);
55 | });
56 |
57 | Handlebars.registerHelper('ifnoteq', function (a, b, options) {
58 | if (a === b) {
59 | return options.fn(this);
60 | }
61 |
62 | return options.inverse(this);
63 | });
64 |
65 | Handlebars.registerPartials = function registerPartials(files) {
66 | const partials = {};
67 | files.forEach((file) => {
68 | if ([`.html`, `.svg`, `.md`].indexOf(file.extension) === -1) {
69 | return;
70 | }
71 |
72 | // calling require here to avoid circular dependencies
73 | // http://selfcontained.us/2012/05/08/node-js-circular-dependencies/
74 | const Render = require('./render');
75 | const fm = Render.extract(file);
76 | const partial = Object.assign({}, file, {
77 | content: fm.content,
78 | variables: fm.fm,
79 | name: file.name.replace('.html', ''),
80 | });
81 | partials[partial.name] = partial;
82 |
83 | Handlebars.registerPartial(partial.name, partial.content);
84 | Handlebars.registerPartial(file.name, partial.content);
85 | });
86 |
87 | return partials;
88 | };
89 |
90 | module.exports = Handlebars;
91 |
--------------------------------------------------------------------------------
/lib/build/render.js:
--------------------------------------------------------------------------------
1 | const fse = require('fs-extra');
2 | const stylus = require('stylus');
3 | const marked = require('marked');
4 | const fm = require('front-matter');
5 | const S = require(`string`);
6 |
7 | // const sass = require(`node-sass`);
8 | const path = require(`path`);
9 | const $log = require('../utils/logger');
10 | const moment = require('moment');
11 |
12 | //@todo: let the user select their preferred template engine
13 | const Handlebars = require('./handlebars-helpers');
14 | const root = process.cwd();
15 | const Render = {};
16 | Render.layouts = {};
17 | Render.partials = {};
18 |
19 | const registerLayouts = function (files) {
20 | for (var k in files) {
21 | if (files[k].name.match('.html') === null) {
22 | return;
23 | }
24 |
25 | var layoutFrontMatter = extract(files[k]);
26 | Object.assign(files[k], layoutFrontMatter);
27 | Render.layouts[k] = files[k];
28 | }
29 |
30 | return Render.layouts;
31 | };
32 |
33 | const registerPartials = function (files) {
34 | Render.partials = Handlebars.registerPartials(files);
35 | };
36 |
37 | const renderCommon = function (page, content, data) {
38 | let template;
39 | try {
40 | template = Handlebars.compile(content);
41 | } catch (e) {
42 | $log.warn(`failed to compile template ${template}`);
43 | }
44 |
45 | let result = template(data);
46 | const newname = (page.name || ``).replace(/\.md$/, '.html');
47 | const destination = {
48 | folder: page.basePath,
49 | file: newname,
50 | };
51 | if (newname !== 'index.html') {
52 | destination.folder = `${page.basePath}${page.name.replace('.html', '')}`;
53 | destination.file = `index.html`;
54 | }
55 |
56 | if (data.page.url) {
57 | const folder = data.page.url;
58 | destination.folder = `${folder}`;
59 | page.name = 'index.html';
60 | } else {
61 | const folder = data.page.title
62 | ? `blog/${S(data.page.category).slugify().s}/${
63 | S(data.page.title).slugify().s
64 | }`
65 | : page.name.replace('.html', '');
66 | destination.folder = `${folder}`;
67 | page.name = 'index.html';
68 | }
69 | data.page.content = result;
70 | const layoutName = page.data.layout || `default`;
71 | if (Render.layouts[layoutName]) {
72 | // g is a namespace for layout and hbs partials' vars
73 | data.page.g = {};
74 | data.page.g.layout = Render.layouts[layoutName].fm;
75 | Object.assign(
76 | data.page.g,
77 | extractPartialsVariables(Render.layouts[layoutName].content),
78 | );
79 | try {
80 | template = Handlebars.compile(Render.layouts[layoutName].content);
81 | } catch (e) {
82 | $log.warn(`Failed to compile file ${template}`);
83 | }
84 | result = template(data);
85 | }
86 |
87 | return {
88 | destination: destination,
89 | content: result,
90 | data: data,
91 | };
92 | };
93 |
94 | const extract = function (file) {
95 | let content = ``;
96 | try {
97 | content = fse.readFileSync(file.path, `utf-8`);
98 | } catch (err) {
99 | if (err) {
100 | throw err;
101 | }
102 | }
103 |
104 | content = fm(content);
105 | const timestamp = moment
106 | .utc(content.date ? content.date : file.stats.birthtime)
107 | .format('X');
108 |
109 | const destination = {
110 | folder: file.basePath,
111 | file: file.name,
112 | };
113 |
114 | return {
115 | destination: destination,
116 | content: content.body,
117 | fm: Object.assign({ timestamp }, content.attributes),
118 | };
119 | };
120 |
121 | const renderStyles = function (item, dest) {
122 | $log.log(`ignoring sass file: ${item.basePath}${item.name}`);
123 | const result = extract(item);
124 | result.css = '';
125 |
126 | // sass.renderSync({
127 | // data: result.content,
128 | // includePaths: [ `${root}/${item.basePath}` ],
129 | // });
130 | return result;
131 | };
132 |
133 | const renderStylus = function (item, dest) {
134 | $log.log(`rendering stylus file: ${item.basePath}${item.name}`);
135 | const result = extract(item);
136 | result.css = stylus(result.content)
137 | .set('paths', [`${root}/${item.basePath}`])
138 | .render();
139 | return result;
140 | };
141 |
142 | const renderHtml = function (page, data) {
143 | data.page = page.data;
144 | return renderCommon(page, page.content, data);
145 | };
146 |
147 | const renderMD = function (page, data) {
148 | data.page = page.data;
149 | const html = marked(page.content);
150 | return renderCommon(page, html, data);
151 | };
152 |
153 | const renderPost = function (page, data) {
154 | let result = {};
155 | const html = marked(page.content);
156 | data.page = page.data;
157 | data.page.url = page.data.url;
158 | result = renderCommon(page, html, data);
159 | result.destination.folder = path.normalize(`${result.destination.folder}`);
160 | return result;
161 | };
162 |
163 | const extractPartialsVariables = function (document) {
164 | var regex = /{{>([\s\w]+)}}/g;
165 | var partialsVars = {};
166 | while ((result = regex.exec(document)) !== null) {
167 | var partialName = result[1].trim();
168 | var currrentPartial = Render.partials[partialName];
169 | if (currrentPartial) {
170 | partialsVars[partialName] = currrentPartial.variables;
171 | }
172 | }
173 |
174 | return partialsVars;
175 | };
176 |
177 | Render['.html'] = renderHtml;
178 | Render['.md'] = renderMD;
179 | Render.post = renderPost;
180 | Render.extract = extract;
181 | Render.registerPartials = registerPartials;
182 | Render.registerLayouts = registerLayouts;
183 | Render.renderStyles = renderStyles;
184 | Render.renderStylus = renderStylus;
185 |
186 | module.exports = Render;
187 |
--------------------------------------------------------------------------------
/lib/commands/build.js:
--------------------------------------------------------------------------------
1 | const logger = require('../utils/logger');
2 |
3 | /**
4 | * Creates the resulting html by reading all the pages and parsing with the layouts
5 | *
6 | * @param {*} project
7 | * @returns project
8 | */
9 | const run = (project) => {
10 | const promise = new Promise((resolve, reject) => {
11 | if (!project.collected || !project.extracted || !project.prebuilt) {
12 | logger.error('Missing prebuilt data at build step');
13 | return reject();
14 | }
15 |
16 | const result = [];
17 |
18 | const _files = project.prebuilt.files;
19 | const _data = project.prebuilt.data;
20 |
21 | _files.map((file, k) => {
22 | result[k] = file.compile_method(file, _files, {
23 | ..._data,
24 | page: {
25 | ...file.item.meta,
26 | ...file.item.attrs,
27 | content: file.item.content,
28 | location: file.destination,
29 | },
30 | });
31 | });
32 |
33 | project.html = result;
34 |
35 | return resolve(project);
36 | });
37 |
38 | return promise;
39 | };
40 |
41 | module.exports = {
42 | run,
43 | };
44 |
--------------------------------------------------------------------------------
/lib/commands/collect.js:
--------------------------------------------------------------------------------
1 | const klaw = require('klaw');
2 | const $q = require('q');
3 | const logger = require('../utils/logger');
4 | const root = process.cwd();
5 | const S = require(`string`);
6 | const path = require('path');
7 |
8 | const _klaw = (folder, items, config) => {
9 | const promise = new Promise(function (resolve, reject) {
10 | klaw(folder)
11 | .on('data', (item) => {
12 | // @TODO: fix bug where is not ignoring excludes
13 | if (item.path.endsWith(config.exclude[0])) {
14 | return;
15 | } else {
16 | const shortPath = S(item.path).chompLeft(`${root}/`).s;
17 | /** excludes the folder it was found in, for copying files as they are usually referenced this way */
18 | const relativePath = S(shortPath).chompLeft(folder).s;
19 |
20 | const details = {
21 | ...item,
22 | shortPath,
23 | relativePath,
24 | };
25 |
26 | items.push(details);
27 | }
28 | })
29 | .on('error', (e) => {
30 | logger.error(e);
31 | reject();
32 | })
33 | .on('end', () => {
34 | resolve();
35 | });
36 | });
37 |
38 | return promise;
39 | };
40 |
41 | // Actual collect file that the bin file calls after it processed the arguments or whatever
42 | const run = (project) => {
43 | const promiseArray = [];
44 |
45 | const items = {
46 | include: [],
47 | copy: [],
48 | styles: [],
49 | theme: [],
50 | };
51 |
52 | project.config.include.map((path) => {
53 | promiseArray.push(_klaw(path, items.include, project.config));
54 | });
55 |
56 | // project.config.copy.map((path) => {
57 | // promiseArray.push(_klaw(path, items.copy, project.config));
58 | // });
59 |
60 | project.config.theme.map((path) => {
61 | promiseArray.push(_klaw(path, items.theme, project.config));
62 | });
63 |
64 | return $q.all(promiseArray).then(() => {
65 | project.collected = items;
66 | return project;
67 | });
68 | };
69 |
70 | module.exports = {
71 | run,
72 | };
73 |
--------------------------------------------------------------------------------
/lib/commands/copy.js:
--------------------------------------------------------------------------------
1 | const klaw = require('klaw');
2 | const $q = require('q');
3 | const logger = require('../utils/logger');
4 |
5 | /**
6 | *
7 | * Didn't abstract this yet coz is slightly different than collect
8 | * and will hopefully change differently
9 | * */
10 | const _klaw = (path, items, config) => {
11 | const promise = new Promise(function (resolve, reject) {
12 | klaw(path)
13 | .on('data', (item) => {
14 | // @TODO: fix bug where is not ignoring excludes
15 | if (item.path.endsWith(config.exclude[0])) {
16 | return;
17 | } else {
18 | items.push(item);
19 | }
20 | })
21 | .on('error', (e) => {
22 | logger.error(e);
23 | reject();
24 | })
25 | .on('end', () => {
26 | resolve();
27 | });
28 | });
29 |
30 | return promise;
31 | };
32 |
33 | // Actual copy method that collects the files
34 | const run = (project) => {
35 | const promiseArray = [];
36 |
37 | const items = {
38 | copy: [],
39 | };
40 |
41 | project.config.copy.map((path) => {
42 | promiseArray.push(_klaw(path, items.copy, project.config));
43 | });
44 |
45 | return $q.all(promiseArray).then(() => {
46 | project.collected = items;
47 | return project;
48 | });
49 | };
50 |
51 | module.exports = {
52 | run,
53 | };
54 |
--------------------------------------------------------------------------------
/lib/commands/css.tailwind.js:
--------------------------------------------------------------------------------
1 | const logger = require('../utils/logger');
2 | const tailwind = require("tailwindcss");
3 | const postcss = require("postcss");
4 | const fs = require('fs');
5 |
6 | /**
7 | * Creates the resulting CSS by reading all the pages and getting the tailwind classes
8 | *
9 | * @param {*} project
10 | * @returns project
11 | */
12 | const run = (project) => {
13 | const promise = new Promise((resolve, reject) => {
14 | const { html, config } = project;
15 | if (!html) {
16 | logger.error('Missing html data at css step');
17 | return reject();
18 | }
19 |
20 | (async () => {
21 | const result = await postcss([
22 | tailwind({
23 | //...config,
24 | content: [`${config.dest}/**/*.{html,js,md}`],
25 | }),
26 | ]).process(`@tailwind base;@tailwind components;@tailwind utilities;`, {
27 | from: undefined,
28 | });
29 |
30 | fs.writeFileSync(`${config.dest}/main.css`, result.css);
31 | })();
32 |
33 | return resolve(project);
34 | });
35 |
36 | return promise;
37 | };
38 |
39 | module.exports = {
40 | run,
41 | };
42 |
--------------------------------------------------------------------------------
/lib/commands/extract.js:
--------------------------------------------------------------------------------
1 | const klaw = require('klaw');
2 | const $q = require('q');
3 | const logger = require('../utils/logger');
4 | const fm = require('front-matter');
5 | const path = require('path');
6 | const fse = require('fs-extra');
7 | const moment = require('moment');
8 | const S = require(`string`);
9 | const root = process.cwd();
10 | const meta = require('../utils/meta');
11 |
12 | /**
13 | * Reads the content of a file to extract the font matter attributes and anything else.
14 | * Traverses the collections of files collected before, including directories
15 | *
16 | * @param {*} file a collected object representing a file in disk
17 | * @param {*} items the collection where to store the extracted information
18 | * @returns {object} attributes with default placeholders for content combined with thouse found
19 | */
20 | const extract = (file, items) => {
21 | const stat = fse.statSync(path.normalize(file.path));
22 |
23 | const item = {
24 | destination: null,
25 | content: null,
26 | stat,
27 | isDir: stat.isDirectory(),
28 | path: file.path,
29 | shortPath: file.shortPath,
30 | relativePath: file.relativePath,
31 | };
32 |
33 | if (item.isDir) {
34 | items.push(item);
35 | return items;
36 | }
37 |
38 | let content = ``;
39 | try {
40 | content = fse.readFileSync(file.path, `utf-8`);
41 | } catch (err) {
42 | if (err) {
43 | throw err;
44 | }
45 | }
46 |
47 | const attrs = meta.read(content);
48 | const timestamp = moment
49 | .utc(attrs.date ? attrs.date : stat.birthtime)
50 | .format('X');
51 |
52 | const destination = {
53 | file: attrs.permalink ? 'index.html' : '',
54 | path: attrs.permalink || item.shortPath,
55 | };
56 |
57 | items.push({
58 | ...item,
59 | attrs,
60 | destination,
61 | content: attrs.raw.body,
62 | meta: { timestamp, ...content.attributes },
63 | });
64 | return items;
65 | };
66 |
67 | /**
68 | * Combines stats and metadata to extract as much information before processing
69 | *
70 | * @param {*} project
71 | * @returns
72 | */
73 | const run = (project) => {
74 | const promise = new Promise((resolve, reject) => {
75 | if (!project.collected || !project.collected.include) {
76 | logger.error('Missing collected property in project at extract step');
77 | return reject();
78 | }
79 |
80 | const items = {
81 | include: [],
82 | theme: [],
83 | copy: [],
84 | };
85 |
86 | project.collected.include.map((c) => {
87 | extract(c, items.include);
88 | });
89 |
90 | project.collected.copy.map((c) => {
91 | extract(c, items.copy);
92 | });
93 |
94 | project.collected.theme.map((c) => {
95 | extract(c, items.theme);
96 | });
97 |
98 | project.extracted = items;
99 | return resolve(project);
100 | });
101 |
102 | return promise;
103 | };
104 |
105 | module.exports = {
106 | run,
107 | };
108 |
--------------------------------------------------------------------------------
/lib/commands/init.js:
--------------------------------------------------------------------------------
1 | // using variable name $log, because most methods are named the same,
2 | // so even plugin in a diff library (if we decide to) is easier
3 |
4 | const $log = require('../utils/logger');
5 | const $q = require(`q`);
6 | const fs = require(`../utils/fs`);
7 | const S = require(`string`);
8 | const prompt = require(`prompt-sync`)();
9 | const chalk = require('chalk');
10 | const Project = require('../core/project');
11 | const version = require('../version');
12 | const path = require(`path`);
13 |
14 | const optionsBuilder = function (argv) {
15 | const layoutsDir = __dirname
16 | .split(`${path.sep}`)
17 | .slice(0, 7)
18 | .concat('layouts')
19 | .join(`${path.sep}`);
20 | if (!fs.existsSync(`${layoutsDir}${path.sep}${argv.layout}`)) {
21 | $log.log(`${argv.layout} doesn't exist, setting layout to default.`);
22 | argv.layout = 'bootstrap';
23 | }
24 |
25 | return {
26 | name: S(argv.name).slugify().s,
27 | location: `${S(argv.name).slugify().s}`,
28 | longname: S(argv.name).humanize().s,
29 | layout: argv.layout || `bootstrap`,
30 | author: '',
31 | };
32 | };
33 |
34 | const inputNameAndLocation = function (argv, project, options) {
35 | if (argv.interactive) {
36 | project.config.name =
37 | options.name || prompt(`Enter short name for the blog: `, options.name);
38 | project.config.location =
39 | options.location ||
40 | prompt(
41 | `Enter a folder name if different from [${options.name}]:`,
42 | options.location,
43 | );
44 | project.config.location = options.location
45 | ? options.location
46 | : `${S(options.name).slugify().s}`;
47 | }
48 |
49 | if (!project.config.name || !project.config.location) {
50 | $log.warn(chalk.red(`A name and location are required`));
51 | return false;
52 | }
53 |
54 | return true;
55 | };
56 |
57 | const inputRest = function (argv, project, options) {
58 | if (argv.interactive) {
59 | project.config.author = options.author || prompt(`Author name:`);
60 | project.config.longname =
61 | options.longname ||
62 | prompt(
63 | `Enter the longname to use in your site's title: [${options.name}]`,
64 | options.name,
65 | );
66 | project.config.description =
67 | options.description ||
68 | prompt(`Enter the description to use in your site:`);
69 | }
70 |
71 | project.config.version = version;
72 | return true;
73 | };
74 |
75 | const showSiteInfo = function (project) {
76 | $log.log(
77 | `\n${chalk.bold.cyan(
78 | 'Site will be created with the following information',
79 | )}`,
80 | );
81 | $log.log(project.getConfig('yaml'));
82 | };
83 |
84 | const confirmSite = function (argv) {
85 | let confirm = 'Y';
86 | if (argv.interactive) {
87 | confirm = prompt(`Looks good?: [Yn]`, `Y`);
88 | }
89 |
90 | if (!(confirm === `y` || confirm === `Y`)) {
91 | return false;
92 | } else {
93 | return true;
94 | }
95 | };
96 |
97 | const fileLocationNotAvailable = function (argv, options) {
98 | const location = `${process.cwd()}${path.sep}${options.location}`;
99 | if (
100 | fs.existsSync(`${location}${path.sep}_config.yml`) &&
101 | argv.force !== true
102 | ) {
103 | confirm = prompt(
104 | chalk.bgRed(`It looks like a site already
105 | exists on that folder,
106 | do you want to overwrite?: [yN]`),
107 | `N`,
108 | );
109 | if (confirm !== `y` && confirm !== `Y`) {
110 | $log.log(chalk.green(`Aborting!`));
111 | return true;
112 | } else {
113 | return false;
114 | }
115 | }
116 | };
117 |
118 | const createSite = function (project) {
119 | project.create({ force: true }).then(
120 | () => {
121 | $log.log(
122 | chalk.green(`Saved!
123 | cd into the directory '${project.config.location}' and run
124 | 'gloria serve' to get started.`),
125 | );
126 | },
127 | (err) => {
128 | $log.log(chalk.red(`Error creating project!`), err);
129 | },
130 | );
131 | };
132 |
133 | const handler = function (argv) {
134 | const options = optionsBuilder(argv);
135 | const project = new Project(options);
136 | $log.log(
137 | `Creating new gloria site.\n` +
138 | (options.name
139 | ? `named: ${options.name}, in folder: ./${options.name}`
140 | : ``),
141 | );
142 | $log.log(`Please complete the following information to continue:`);
143 | if (!inputNameAndLocation(argv, project, options)) {
144 | return;
145 | }
146 |
147 | inputRest(argv, project, options);
148 | showSiteInfo(project);
149 | if (!confirmSite(argv)) {
150 | return;
151 | }
152 |
153 | if (fileLocationNotAvailable(argv, options)) {
154 | return;
155 | }
156 |
157 | createSite(project);
158 | };
159 |
160 | const builder = {
161 | name: {
162 | default: ``,
163 | description: `Short name to use in the site's configuration, folder name`,
164 | },
165 | longname: {
166 | default: ``,
167 | description: `A longer name to use in your site's content`,
168 | },
169 | description: {
170 | default: ``,
171 | description: `Set a description for your site`,
172 | },
173 | location: {
174 | default: ``,
175 | description: `Folder where your files and site will be at`,
176 | },
177 | layout: {
178 | default: `bootstrap`,
179 | description: `There are two layouts currently available:
180 | Bootstrap, includes bootstrap css files with several themes form bootswatch
181 | Default, is basically an empty layout.`,
182 | },
183 | interactive: {
184 | default: true,
185 | description: `Use the interactive prompt to complete the details`,
186 | },
187 | force: {
188 | default: false,
189 | description: `If files exists, overwrite them?`,
190 | },
191 | };
192 |
193 | module.exports = {
194 | command: `init [name]`,
195 | aliases: [`create`],
196 | describe: `Initializes a new site, interactively or using the given parameters.
197 | It will create a base configuration file and sample pages using the desired layout.`,
198 | builder: builder,
199 | handler: handler,
200 | optionsBuilder: optionsBuilder,
201 | showSiteInfo: showSiteInfo,
202 | confirmSite: confirmSite,
203 | inputNameAndLocation: inputNameAndLocation,
204 | inputRest: inputRest,
205 | fileLocationNotAvailable: fileLocationNotAvailable,
206 | createSite: createSite,
207 | };
208 |
--------------------------------------------------------------------------------
/lib/commands/migrate.js:
--------------------------------------------------------------------------------
1 | const $log = require('../utils/logger');
2 | const chalk = require(`chalk`);
3 | const fse = require(`fs-extra`);
4 | const S = require(`string`);
5 | const path = require(`path`);
6 | const fs = require(`../utils/fs`);
7 |
8 | const Project = require('../core/project');
9 | const project = new Project();
10 |
11 | const root = process.cwd();
12 |
13 | function handler(argv) {
14 | if (argv.source !== 'jekyll') {
15 | return $log.warn(chalk.red(`Sorry, only jekyll is supported now`));
16 | }
17 |
18 | let dest = project.config.dest || argv.dest;
19 | project.loadConfigFromYamlFile();
20 | project.change({
21 | dest: dest,
22 | longname: project.config.title,
23 | });
24 | project.saveYAML(true, root);
25 | dest = `${root}/${dest}`;
26 |
27 | if (argv.exporto.lastIndexOf(`..`, 0) === 0) {
28 | $log.log(`Copying files to destination folder ${root}/${argv.exporto}`);
29 | fse.copySync(root, dest);
30 | }
31 |
32 | fse.walk(`${root}`).on('data', (item) => {
33 | if (S(item.path).include(dest)) {
34 | return;
35 | }
36 |
37 | item.isDirectory = item.stats.isDirectory();
38 | item.shortPath = S(item.path).chompLeft(root).s;
39 | item.name = path.basename(item.path);
40 | item.basePath = S(item.shortPath).chompRight(item.name).s;
41 | item.extension = path.extname(item.path);
42 | if (
43 | item.shortPath.lastIndexOf(`/.git`, 0) === 0 &&
44 | item.name !== `.gitignore`
45 | ) {
46 | return;
47 | }
48 |
49 | if (item.shortPath.lastIndexOf(`/_site`, 0) === 0) {
50 | return;
51 | }
52 |
53 | if ([`.html`, `.md`].indexOf(item.extension) !== -1) {
54 | $log.log(`migrating file`, item.path);
55 | let content = fse.readFileSync(item.path, `utf-8`);
56 |
57 | // content = convert('{liquid}')
58 | content = content.replace(/{%\s*include/g, `{{>`);
59 | content = content.replace(/{%\s*if/g, `{{#if`);
60 | content = content.replace(/{%\s*else/g, `{{^`);
61 | content = content.replace(/{%\s*endif/g, `{{/if`);
62 | content = content.replace(/\|.+}}/g, `}}`);
63 | content = content.replace(/{%/g, `{{`);
64 | content = content.replace(/%}/g, `}}`);
65 | try {
66 | fs.writeFileSync(item.path, content);
67 | } catch (err) {
68 | throw err;
69 | }
70 |
71 | $log.log(`Migrated file `, item.path);
72 | }
73 | });
74 | }
75 |
76 | const builder = {
77 | source: {
78 | default: `jekyll`,
79 | description: `Source platform.`,
80 | },
81 | exporto: {
82 | default: false,
83 | description: `When specified, the folder where the new files will be exported`,
84 | },
85 | dest: {
86 | default: 'docs',
87 | },
88 | };
89 |
90 | module.exports = {
91 | command: `migrate [source]`,
92 | aliases: [],
93 | describe: `Migrates an existing website from a different platform to gloria.
94 | I'ts pretty buggy now and only works with jekyll.
95 | The replacement is pretty poor right now, it uses regex to find some liquid tags
96 | and replaces them with handlebars. It ignores some helpers like loops.
97 | It requires some extra manual work. If that's not cool with you, please consider a PR.`,
98 | builder: builder,
99 | handler: handler,
100 | };
101 |
--------------------------------------------------------------------------------
/lib/commands/new.js:
--------------------------------------------------------------------------------
1 | /** @module new */
2 |
3 | const $log = require('../utils/logger');
4 | const S = require('string');
5 | const chalk = require('chalk');
6 | const path = require('path');
7 |
8 | // the object meta holds information about the different types that can be
9 | // created, like it's location, template and whether or not it uses frontmatter
10 |
11 | const meta = require('./new/meta');
12 | const fs = require('../utils/fs');
13 | const createContent = require(`./new/create-content`);
14 |
15 | /** @function
16 | * @name handler
17 | *
18 | * @param argv Command line arguments, pased to this function by the argv package
19 | */
20 | function handler(argv) {
21 | const options = argv;
22 | options.name = S(options.name || options.title).dasherize().s;
23 | if (!meta[options.type]) {
24 | return $log.error(
25 | chalk.red(`Invalid type provided, please use one of:
26 | post|page|layout|partial|sass|css|public`),
27 | );
28 | }
29 |
30 | Object.assign(options, meta[options.type]);
31 | options.ext = path.extname(options.name)
32 | ? path.extname(options.name)
33 | : options.ext;
34 | options.path = `.${path.sep}${options.folder + path.sep}${options.dest}`;
35 | options.fullPath = `${options.path}${options.name}${options.ext}`;
36 | const file = createContent(options);
37 |
38 | fs.ensureDir(path.dirname(options.fullPath))
39 | .then(function () {
40 | if (argv.verbose) {
41 | $log.log(
42 | chalk.green(`Folder ${path.dirname(options.fullPath)} verified.`),
43 | );
44 | }
45 |
46 | return fs.writeFile(options.fullPath, file.content);
47 | })
48 | .then(function (data, err) {
49 | if (argv.verbose) {
50 | $log.log(chalk.green(`File ${options.fullPath} created.`));
51 | }
52 | });
53 |
54 | return options;
55 | }
56 |
57 | const builder = {
58 | type: {
59 | default: `post`,
60 | description: `Type of content to create.
61 | post|page|layout|partial|sass|css|public are available.`,
62 | },
63 | title: {
64 | default: '',
65 | description: 'Title for the page or content.',
66 | },
67 | name: {
68 | default: '',
69 | description: 'Name for the file, if none is provided, title will be used.',
70 | },
71 | description: {
72 | default: '',
73 | description: 'Description for the page or content.',
74 | },
75 | category: {
76 | default: '',
77 | description: 'Category, usually included in posts.',
78 | },
79 | verbose: {
80 | default: true,
81 | description: `Supress logs and warnings`,
82 | },
83 | folder: {
84 | default: '',
85 | description: `Can be used to prepend to the directory where the file is created.`,
86 | type: 'path',
87 | },
88 | };
89 |
90 | module.exports = {
91 | command: `new [type] [title]`,
92 | aliases: [`n`],
93 | describe: `Creates new content, with different templates, depending on the type.
94 | Examples:
95 | gloria new post hello-world
96 | gloria new --type=page --title='Contact Us' --description='Contact form.' `,
97 | builder: builder,
98 | handler: handler,
99 | };
100 |
--------------------------------------------------------------------------------
/lib/commands/new/create-content.js:
--------------------------------------------------------------------------------
1 | /** @module new/createContent
2 | *
3 | * It has the methods necesary to build content for the new command,
4 | * it uses the options and templates pre-defined to create the file.
5 | * It will return the string that will be the content of the file.
6 | */
7 |
8 | const YAML = require('yamljs');
9 |
10 | const buildHeaders = function buildHeaders(options) {
11 | return {
12 | title: options.title || '',
13 | description: options.description || '',
14 | type: options.type || '',
15 | layout: options.layout || '',
16 | category: options.category || '',
17 | url: (options.category || '') + options.name,
18 | };
19 | };
20 |
21 | module.exports = function createContent(options) {
22 | let headers;
23 | let content = ``;
24 | const body = ``;
25 | if (options.fm !== false) {
26 | headers = buildHeaders(options);
27 | content = `---\n${YAML.stringify(headers)}\n---`;
28 | }
29 |
30 | return {
31 | headers: headers,
32 | body: body,
33 | content: content,
34 | };
35 | };
36 |
--------------------------------------------------------------------------------
/lib/commands/new/meta.js:
--------------------------------------------------------------------------------
1 | /** @module meta
2 | *
3 | * Is an object that holds the information required by the `new` command.
4 | * Every type of content that can be created will have options like location,
5 | * extension and whether or not it uses frontmatter
6 | */
7 |
8 | const path = require(`path`);
9 |
10 | module.exports = {
11 | page: { dest: ``, ext: `.html` },
12 | post: { dest: `_posts${path.sep}`, ext: `.html` },
13 | partial: { dest: `_includes${path.sep}`, fm: false, ext: `.html` },
14 | include: { dest: `_includes${path.sep}`, fm: false, ext: `.html` },
15 | layout: { dest: `_layout${path.sep}`, fm: false, ext: `.html` },
16 | sass: { dest: `_sass${path.sep}`, fm: false, ext: `.scss` },
17 | css: { dest: `_public${path.sep}css${path.sep}`, fm: false, ext: `.css` },
18 | public: { dest: `_public${path.sep}`, fm: false },
19 | };
20 |
--------------------------------------------------------------------------------
/lib/commands/prebuild.js:
--------------------------------------------------------------------------------
1 | const klaw = require('klaw');
2 | const $q = require('q');
3 | const logger = require('../utils/logger');
4 | const fm = require('front-matter');
5 | const path = require('path');
6 | const fse = require('fs-extra');
7 | const moment = require('moment');
8 | const S = require(`string`);
9 | const root = process.cwd();
10 | const meta = require('../utils/meta');
11 | const compilers = require('../utils/compilers').compilers;
12 | const rejectPrebuild = require('../utils/filters').rejectPrebuild;
13 |
14 | /**
15 | * Reads from the available information and generates a string where the file goes
16 | * @param {*} item
17 | * @param {string} item.destination.path
18 | * @param {string} item.destination.file
19 | * @returns {string} destination to write content to
20 | */
21 | const calculateDestination = (item) => {
22 | const { destination } = item;
23 | const { path, file } = destination;
24 |
25 | if (item.shortPath.endsWith('CNAME')) {
26 | return 'CNAME';
27 | }
28 |
29 | if (!path && !file) {
30 | return '';
31 | }
32 |
33 | if (!path) {
34 | return '' + file;
35 | }
36 |
37 | if (typeof path !== 'string') {
38 | return '';
39 | }
40 |
41 | if (typeof path === 'string' && typeof file === 'string') {
42 | return `${destination.path.replace(/\/$/, '')}/${destination.file}`.replace(
43 | /\/$/,
44 | '',
45 | );
46 | }
47 |
48 | return 'index.html';
49 | };
50 |
51 | /**
52 | * Reads the content of a file to extract the font matter attributes and anything else.
53 | * Traverses the collections of files collected before, including directories
54 | *
55 | * @param {*} file a collected object representing a file in disk
56 | * @param {*} items the collection where to store the extracted information
57 | * @returns {object} attributes with default placeholders for content combined with thouse found
58 | */
59 | const prebuild = (item, items) => {
60 | const result = {
61 | item,
62 | compile_method: '',
63 | };
64 |
65 | if (rejectPrebuild(item)) {
66 | logger.log(`Ignoring ${item.shortPath}`);
67 | return items;
68 | }
69 |
70 | result.destination = calculateDestination(item);
71 | result.compile_method = compilers(item);
72 |
73 | items.files.push(result);
74 |
75 | return items;
76 | };
77 |
78 | /**
79 | * Combines stats and metadata to extract as much information before processing
80 | *
81 | * @param {*} project
82 | * @returns
83 | */
84 | const run = (project) => {
85 | const promise = new Promise((resolve, reject) => {
86 | if (!project.collected || !project.collected.include) {
87 | logger.error('Missing collected property in project at extract step');
88 | return reject();
89 | }
90 |
91 | /**
92 | * Contains all the files, the keys are the path where it will be written.
93 | * items with matching urls will be overwritten by others processed later
94 | * is not a bug but it can confuse people
95 | */
96 | const items = {
97 | styles: {},
98 | scripts: {},
99 | files: [],
100 | data: {
101 | site: project.config,
102 | head: {},
103 | },
104 | };
105 |
106 | project.extracted.theme.map((c) => {
107 | prebuild(c, items);
108 | });
109 |
110 | project.extracted.copy.map((c) => {
111 | prebuild(c, items);
112 | });
113 |
114 | project.extracted.include.map((c) => {
115 | prebuild(c, items);
116 | });
117 |
118 | project.prebuilt = items;
119 |
120 | return resolve(project);
121 | });
122 |
123 | return promise;
124 | };
125 |
126 | module.exports = {
127 | run,
128 | };
129 |
--------------------------------------------------------------------------------
/lib/commands/serve.js:
--------------------------------------------------------------------------------
1 | const $log = require('../utils/logger');
2 | const chalk = require(`chalk`);
3 | const express = require('express');
4 | const path = require('path');
5 | const watch = require('node-watch');
6 | const Project = require('../core/project');
7 | const project = new Project();
8 | const build = require(`./build`);
9 | const open = require('open');
10 | const root = process.cwd();
11 |
12 | const initializeSite = function (dest) {
13 | $log.log(`Preparing to serve static files from: ${root}${path.sep}${dest}`);
14 | return express();
15 | };
16 |
17 | const serveStaticAssets = function (app, dest) {
18 | return app.use(express.static(`${root}${path.sep}${dest}`));
19 | };
20 |
21 | const setRoutes = function (app, dest) {
22 | return app.all('*', (req, res, next) => {
23 | $log.log('loading: ', req.originalUrl);
24 | res.sendFile(`${root}${path.sep}${dest}/404.html`);
25 | });
26 | };
27 |
28 | const serveAndBuildSite = function (argv, dest) {
29 | const app = initializeSite(dest);
30 | argv.dest = dest;
31 | build.handler(argv);
32 | serveStaticAssets(app, dest);
33 | setRoutes(app, dest);
34 | return app;
35 | };
36 |
37 | const watchSourceFiles = function (dest, argv) {
38 | const ignore = new RegExp(`node_modules|${dest}|\.git`);
39 | $log.info(chalk.cyan(`Watching ${root} folder, ignoring ${ignore}`));
40 | watch(
41 | root,
42 | {
43 | filter: (name) => !ignore.test(name),
44 | },
45 | (file) => {
46 | $log.log(`File`, file, ` changed`);
47 | try {
48 | build.handler(Object.assign({ save: false }, argv));
49 | } catch (e) {
50 | $log.log('Failed building project');
51 | $log.log(e);
52 | }
53 | },
54 | );
55 | };
56 |
57 | const launchSite = function (argv, app, dest) {
58 | // Using an arbitrary number to wait for files to build
59 | setTimeout(() => {
60 | if (argv.watch === true) {
61 | watchSourceFiles(dest, argv);
62 | }
63 |
64 | app.listen(argv.port, function () {
65 | $log.warn(
66 | chalk.green(`Serving static files from ${root}${path.sep}${dest}`),
67 | );
68 | $log.warn(chalk
69 | .bold.red`Never use this in production. See the deployment section
70 | in the documentation for more information about deployment.`);
71 | $log.warn(chalk.green(`listening in port ${argv.port}`));
72 | if (!argv.suppressBrowser) {
73 | open('http://localhost:' + argv.port);
74 | }
75 | });
76 | }, 1 * 1000);
77 | };
78 |
79 | const handler = function (argv) {
80 | argv = argv || {};
81 | if (argv.silent) {
82 | $log.silent();
83 | }
84 |
85 | const options = project.loadConfig('yaml');
86 | const dest = project.config.dest || argv.dest;
87 | const app = serveAndBuildSite(argv, dest);
88 | launchSite(argv, app, dest);
89 | };
90 |
91 | const builder = {
92 | dest: {
93 | default: `site`,
94 | description: `Destination path or folder to serve the site from.`,
95 | },
96 | port: {
97 | default: '3300',
98 | description: `Port on which to serve the site.`,
99 | },
100 | 'suppress-browser': {
101 | default: false,
102 | description: `Don't open the browser automatically.`,
103 | type: 'boolean',
104 | alias: 'b',
105 | },
106 | watch: {
107 | default: true,
108 | description: `Watchs the source files for changes, and re-builds the site.`,
109 | alias: 'w',
110 | type: 'boolean',
111 | },
112 | clear: {
113 | default: false,
114 | description: `Removes the current content of the source directory`,
115 | alias: 'c',
116 | type: 'boolean',
117 | },
118 | silent: {
119 | default: false,
120 | description: `Limit the amount of output to the console.`,
121 | alias: 's',
122 | type: 'boolean',
123 | },
124 | };
125 |
126 | module.exports = {
127 | command: `serve [dest]`,
128 | aliases: [],
129 | describe: `Serves the site from the last known destination, or from the specific folder given.`,
130 | builder: builder,
131 | handler: handler,
132 | serveAndBuildSite: serveAndBuildSite,
133 | initializeSite: initializeSite,
134 | serveStaticAssets: serveStaticAssets,
135 | setRoutes: setRoutes,
136 | watchSourceFiles: watchSourceFiles,
137 | launchSite: launchSite,
138 | };
139 |
--------------------------------------------------------------------------------
/lib/commands/setup.js:
--------------------------------------------------------------------------------
1 | const $q = require('q');
2 | const logger = require('../utils/logger');
3 | const config = require('../core/project.config');
4 | const root = process.cwd();
5 | const path = require('path');
6 |
7 | // Actual collect file that the bin file calls after it processed the arguments or whatever
8 | const run = (project) => {
9 | const { writeYaml } = config;
10 |
11 | // @TODO: create folders described in _config files to avoid errors during first run
12 |
13 | writeYaml(project.config);
14 | logger.log(project.config);
15 | return $q.resolve();
16 | };
17 |
18 | module.exports = {
19 | run,
20 | };
21 |
--------------------------------------------------------------------------------
/lib/commands/write.html.js:
--------------------------------------------------------------------------------
1 | const logger = require('../utils/logger');
2 | const upath = require('upath');
3 | const fs = require('fs');
4 |
5 | /**
6 | * Writes the HTHL property from the project to disk
7 | *
8 | * @param {*} project
9 | * @returns project
10 | */
11 | const run = (project) => {
12 | const promise = new Promise((resolve, reject) => {
13 | const { html, config } = project;
14 | const { dest } = config;
15 |
16 | if (!html) {
17 | logger.error('Missing html data at css step');
18 | return reject();
19 | }
20 |
21 | if (!fs.existsSync(dest)) {
22 | fs.mkdirSync(dest, { recursive: true });
23 | }
24 |
25 | html.map((file) => {
26 | const destination = upath.normalize(`${dest}/${file.destination}`);
27 |
28 | const parsed = upath.parse(destination);
29 |
30 | logger.log({ "Looking for dir:": parsed.dir });
31 | if (!fs.existsSync(parsed.dir)) {
32 | logger.log({ "Created dir:": parsed.dir });
33 | fs.mkdirSync(parsed.dir, { recursive: true });
34 | }
35 |
36 | logger.log({ "Writing to dest:": destination });
37 |
38 | fs.writeFileSync(destination, file.content);
39 | });
40 |
41 | // project.extracted.copy.map((file) => {
42 | // const destination = upath.normalize(`${dest}/${file.relativePath}`);
43 | // const parsed = upath.parse(destination);
44 |
45 | // if (parsed.dir && !fs.existsSync(parsed.dir)) {
46 | // fs.mkdirSync(parsed.dir, { recursive: true });
47 | // }
48 |
49 | // if (!file.isDir) {
50 | // fs.writeFileSync(destination, file.content);
51 | // }
52 |
53 | // });
54 |
55 | return resolve(project);
56 | });
57 |
58 | return promise;
59 | };
60 |
61 | module.exports = {
62 | run,
63 | };
64 |
--------------------------------------------------------------------------------
/lib/core/project.config.js:
--------------------------------------------------------------------------------
1 | const logger = require('../utils/logger');
2 | const { v4: uuidv4 } = require('uuid');
3 | const yaml = require(`yamljs`);
4 | const fs = require(`../utils/fs`);
5 | const path = require('path');
6 | // import chalk = require('chalk');
7 |
8 | const loadConfigFromYamlFile = () => {
9 | var root = process.cwd();
10 | if (!fs.existsSync(path.normalize(`${root}/_config.yml`))) {
11 | //require(`yargs`)
12 | logger.warn(`${root}/_config.yml was not found`);
13 | return {};
14 | }
15 |
16 | const config = yaml.load(path.normalize(`${root}/_config.yml`));
17 |
18 | return config;
19 | };
20 |
21 | const writeConfigToYamlFile = (config, overwrite = false) => {
22 | var root = process.cwd();
23 | const configPath = path.normalize(`${root}/_config.yml`);
24 |
25 | if (!fs.existsSync(configPath)) {
26 | //require(`yargs`)
27 | logger.warn(`${root}/_config.yml already exists`);
28 | return {};
29 | }
30 |
31 | const configString = yaml.stringify(config);
32 |
33 | fs.writeFile(path.normalize(`${root}/_config.yml`), configString, (err) => {
34 | if (err) {
35 | return logger.error(err);
36 | }
37 | logger.log('_config.yml was created!');
38 | });
39 | return {};
40 | };
41 |
42 | const defaults = {
43 | include: ['pages'],
44 | name: 'Slava',
45 | tagline: 'A website generated with gloriaJs',
46 | author: 'gloriajs',
47 | dest: 'build',
48 | copy: ['public'],
49 | exclude: ['.draft.md'],
50 | theme: ['layouts/tailwind'],
51 | css: 'tailwind',
52 | engine: 'default',
53 | version: '2',
54 | };
55 |
56 | /**
57 | * Call to read project configuration information from the defaults and the config files
58 | *
59 | * @returns {object} configuration settings for the project
60 | */
61 | const get = () => {
62 | const defaultConfig = defaults;
63 | const yamlConfig = loadConfigFromYamlFile();
64 |
65 | /**
66 | * Configuration settings for the project.
67 | * Combined settings from defaults and _config.yml
68 | */
69 | const config = {
70 | _hash: uuidv4(),
71 | ...defaultConfig,
72 | ...yamlConfig,
73 | };
74 |
75 | return config;
76 | };
77 |
78 | module.exports = {
79 | get: get,
80 | getYaml: loadConfigFromYamlFile,
81 | writeYaml: writeConfigToYamlFile,
82 | defaults,
83 | };
84 |
--------------------------------------------------------------------------------
/lib/core/project.js:
--------------------------------------------------------------------------------
1 | const $log = require('../utils/logger');
2 | const $q = require(`q`);
3 | const yaml = require(`yamljs`);
4 | const fs = require(`../utils/fs`);
5 | // const chalk = require(`chalk`);
6 | const path = require(`path`);
7 |
8 | function Project(config) {
9 | this.config = config || {};
10 | this.pages = [];
11 | this.posts = [];
12 | this.count = {
13 | others: 0,
14 | };
15 | }
16 |
17 | Project.prototype.getConfig = function (format) {
18 | if (format === 'yaml') {
19 | return yaml.stringify(this.config, 4);
20 | }
21 | return {};
22 | };
23 |
24 | Project.prototype.loadConfigFromYamlFile = function () {
25 | var root = process.cwd();
26 | if (!fs.existsSync(path.normalize(`${root}/_config.yml`))) {
27 | $log.warn((`${root}/_config.yml was not found`));
28 | return;
29 | }
30 |
31 | const config = yaml.load(path.normalize(`${root}/_config.yml`));
32 | if (
33 | fs.existsSync(path.normalize(`${root}/themes/${config.theme}/_config.yml`))
34 | ) {
35 | this.themeConfig = yaml.load(
36 | path.normalize(`${root}/themes/${config.theme}/_config.yml`),
37 | );
38 | }
39 |
40 | this.config = Object.assign({}, this.themeConfig, config);
41 | return this.config;
42 | };
43 |
44 | Project.prototype.loadConfig = function (format) {
45 | if (format === 'yaml') {
46 | return this.loadConfigFromYamlFile();
47 | }
48 | };
49 |
50 | Project.prototype.getLocation = function () {
51 | if (!this.config.location) {
52 | throw 'invalid location in configuration';
53 | }
54 |
55 | return `${process.cwd()}/${this.config.location}`;
56 | };
57 |
58 | Project.prototype.change = function (options) {
59 | Object.assign(this.config, options);
60 | return options;
61 | };
62 |
63 | Project.prototype.saveYAML = function (force, location) {
64 | const deferred = $q.defer();
65 | location = location || this.getLocation();
66 | $log.log(`Saving configuration to file ${location}/_config.yml`);
67 | if (fs.existsSync(`${location}/_config.yml`) && force !== true) {
68 | return $q.reject({ error: `Project exist, won't override` });
69 | }
70 |
71 | fs.writeFileSync(`${location}/_config.yml`, this.getConfig(`yaml`), {
72 | flag: 'w',
73 | });
74 | return $q.when({});
75 | };
76 |
77 | Project.prototype.create = function (options) {
78 | options = options || {};
79 | const _this = this;
80 | const deferred = $q.defer();
81 | let layout = _this.config.layout || 'default';
82 | layout = `${__dirname}/../../layouts/${this.config.layout}/`;
83 | var location = this.getLocation();
84 |
85 | if (!_this.config) {
86 | return $q.reject({ error: `Invalid configuration data` });
87 | }
88 |
89 | if (!_this.config.location) {
90 | return $q.reject({ error: `Invalid location given` });
91 | }
92 |
93 | if (
94 | fs.existsSync(`${_this.config.location}/_config.yml`) &&
95 | options.force !== true
96 | ) {
97 | return $q.reject({ error: `Project exist, won't override` });
98 | }
99 |
100 | if (!fs.existsSync(location)) {
101 | fs.mkdirSync(location);
102 | $log.log(`Created new folder ${location}`);
103 | }
104 |
105 | if (!fs.existsSync(layout)) {
106 | return $q.reject({
107 | error: `Selected layout ${_this.config.layout} doesn't exist`,
108 | });
109 | }
110 |
111 | fs.copy(layout, location).then(function (err) {
112 | if (err) {
113 | return deferred.reject({
114 | error: `failed copying layout files`,
115 | err: err,
116 | });
117 | }
118 |
119 | $log.log(`Copied layout files into project folder.`);
120 | _this.saveYAML(options.force);
121 | deferred.resolve();
122 | });
123 |
124 | return deferred.promise;
125 | };
126 |
127 | module.exports = Project;
128 |
--------------------------------------------------------------------------------
/lib/utils/compilers.js:
--------------------------------------------------------------------------------
1 | const path = require('path');
2 | const marked = require('marked');
3 | const hbs = require('./handlebars');
4 | const logger = require('./logger');
5 |
6 | /**
7 | * Find layout file from collected ones, and parse html to interpolate the current file into it
8 | * @param {*} item
9 | * @param {*} files
10 | * @returns html string
11 | */
12 | const getLayoutHTML = (item, files) => {
13 | let html = '{{{content}}}';
14 |
15 | if (!item.attrs.layout) {
16 | return html;
17 | }
18 |
19 | const layout_file = files.find(
20 | (file) => file.item.attrs.name === item.attrs.layout,
21 | );
22 |
23 | if (layout_file) {
24 | html = marked.parse(layout_file.item.content);
25 | }
26 |
27 | return html;
28 | };
29 |
30 | /**
31 | * Interpolates the content of the paage and available data into the layout and returns the full content of the page
32 | * @param {*} layout
33 | * @param {*} item
34 | * @param {*} data
35 | * @param {[]} files
36 | * @returns html string
37 | */
38 | const interpolateLayoutAndContent = (layout, item, data, files) => {
39 | // currently no support for nested layouts
40 | const html = marked.parse(item.content);
41 | // compile template using site data and content of page
42 | const layout_template = hbs.compile(layout);
43 | const layout_with_html = layout_template({ ...data, content: html, files });
44 |
45 | // replace variables with values (handlebars) in full content
46 | const template = hbs.compile(layout_with_html);
47 | const result = template({ ...data, files });
48 |
49 | return result;
50 | };
51 |
52 | /**
53 | * Has a dictionary with keys as the file extensions and methods that process the content
54 | * of the file to create the pages, currently supporting markdown.
55 | * CSS due to tailwind, is processed globally after passing thru all the items
56 | *
57 | * @param item {object}
58 | */
59 | const compilers = (item) => {
60 | const ext = path.extname(item.shortPath);
61 |
62 | const noop = (file) => {
63 | const { item, destination } = file;
64 | return {
65 | destination,
66 | content: item.content || '',
67 | };
68 | };
69 |
70 | const options = {
71 | '.md': function (file, files, data) {
72 | const { item, destination } = file;
73 | // get html from layout
74 | const layout = getLayoutHTML(item, files);
75 | // compile md to html and include in layout
76 | const result = interpolateLayoutAndContent(layout, item, data, files);
77 | logger.info(`Compiled: ${item.shortPath}`);
78 |
79 | return {
80 | destination,
81 | content: result,
82 | };
83 | },
84 | '.html': function (file, files, data) {
85 | const { item, destination } = file;
86 | // get html from layout
87 | const layout = getLayoutHTML(item, files);
88 | // compile md to html and include in layout
89 | const result = interpolateLayoutAndContent(layout, item, data, files);
90 | logger.info(`Compiled: ${item.shortPath}`);
91 |
92 | return {
93 | destination,
94 | content: result,
95 | };
96 | },
97 | };
98 |
99 | if (options[ext]) {
100 | return options[ext];
101 | }
102 | console.log(`${item.shortPath} has no associated compiler`);
103 |
104 | return noop;
105 | };
106 |
107 | module.exports = {
108 | compilers,
109 | };
110 |
--------------------------------------------------------------------------------
/lib/utils/configstore.js:
--------------------------------------------------------------------------------
1 | const Configstore = require('configstore');
2 | var pkg = require('../../package.json');
3 |
4 | // Init a Configstore instance with an unique ID eg. package name
5 | // and optionally some default values
6 | module.exports = new Configstore(pkg.name);
7 |
--------------------------------------------------------------------------------
/lib/utils/filters.js:
--------------------------------------------------------------------------------
1 | /**
2 | * Contains the rules we use to filter files during the prebuild process
3 | * @param {object} item representing a file and its attributes
4 | */
5 | const rejectPrebuild = (item /* project */) => {
6 | if (item.isDir) {
7 | return true;
8 | }
9 |
10 | if (item.path.endsWith('.draft.md')) {
11 | return true;
12 | }
13 | };
14 |
15 | module.exports = {
16 | rejectPrebuild,
17 | };
18 |
--------------------------------------------------------------------------------
/lib/utils/fs.js:
--------------------------------------------------------------------------------
1 | var fs = require('fs-promise');
2 |
3 | module.exports = fs;
4 |
--------------------------------------------------------------------------------
/lib/utils/handlebars.js:
--------------------------------------------------------------------------------
1 | const hbs = require("handlebars");
2 | const logger = require('../utils/logger');
3 |
4 | const createFilter = (conditions, context, options) => {
5 | let s = '';
6 | const [filter_key, filter_value] = conditions;
7 |
8 | const filtered = context.reverse().filter((a) => {
9 | return a.item.attrs[filter_key] === filter_value;
10 | });
11 |
12 | filtered.forEach((item) => {
13 | s += options.fn(item);
14 | });
15 |
16 | return s;
17 | };
18 |
19 | /**
20 | * @param {array} items the pages attribute after the prebuild step
21 | *
22 | * @returns {string} a list of blog posts in creation order html with the interpolated block
23 | */
24 | hbs.registerHelper('blog_posts', (context, options) => {
25 | return createFilter(['type', 'post'], context, options);
26 | });
27 |
28 | /**
29 | * @param {array} items the pages attribute after the prebuild step
30 | *
31 | * @returns {string} a list of blog posts in creation order html with the interpolated block
32 | */
33 | hbs.registerHelper('pages_in_nav', (context, options) => {
34 | return createFilter(['nav', true], context, options);
35 | });
36 |
37 | module.exports = hbs;
38 |
--------------------------------------------------------------------------------
/lib/utils/logger.js:
--------------------------------------------------------------------------------
1 | const defaultLoggers = {
2 | log: console.log,
3 | info: console.info,
4 | warn: console.warn,
5 | error: console.error,
6 | time: console.time,
7 | timeEnd: console.timeEnd,
8 | profile: () => '',
9 | };
10 |
11 | const logger = defaultLoggers;
12 |
13 | logger.verbose = Object.assign({}, defaultLoggers);
14 |
15 | logger.silent = function () {
16 | logger.log = () => '';
17 | logger.info = () => '';
18 | };
19 |
20 | module.exports = logger;
21 |
--------------------------------------------------------------------------------
/lib/utils/meta.js:
--------------------------------------------------------------------------------
1 | const fm = require('front-matter');
2 |
3 | /**
4 | * Returns the front matter metadata included in the file's content
5 | *
6 | * @param {string} content string with the file content to read front matter attributes
7 | *
8 | * @returns {object} that has placeholders for all the attributes combined with the extracted ones
9 | */
10 | const read = (content) => {
11 | const attrs = {
12 | links_from: [],
13 | links_to: [],
14 | title: '',
15 | name: '',
16 | layout: '',
17 | description: '',
18 | tags: [],
19 | category: '',
20 | url: '',
21 | raw: {},
22 | };
23 |
24 | const data = fm(content);
25 |
26 | /** Good place to extract tags, categories and other things, so they can be classified later */
27 | return {
28 | ...attrs,
29 | ...data.attributes,
30 | raw: data,
31 | url: data.attributes.permalink,
32 | };
33 | };
34 |
35 | module.exports = {
36 | read,
37 | };
38 |
--------------------------------------------------------------------------------
/lib/version.js:
--------------------------------------------------------------------------------
1 | module.exports = require('../package.json').version;
2 |
--------------------------------------------------------------------------------
/logs/.gitkeep:
--------------------------------------------------------------------------------
1 | i
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "gloriajs",
3 | "version": "2.2.3",
4 | "description": "Gloria is a NodeJS simple static website generator.",
5 | "main": "cli.js",
6 | "bin": "cli.js",
7 | "scripts": {
8 | "test": "touch .",
9 | "tsc": "tsc",
10 | "lint": "eslint '**/*.{js,ts}' --fix",
11 | "prettier": "prettier --write .",
12 | "build": "tsc ",
13 | "gloria": "./bin/gloria"
14 | },
15 | "repository": {
16 | "type": "git",
17 | "url": "git+https://github.com/gloriajs/gloria.git"
18 | },
19 | "author": "dvidsilva",
20 | "license": "SEE LICENSE IN LICENSE",
21 | "bugs": {
22 | "url": "https://github.com/gloriajs/gloria/issues"
23 | },
24 | "homepage": "https://gloriajs.github.io.com/",
25 | "dependencies": {
26 | "@types/yargs": "^17.0.19",
27 | "async": "^2.1.2",
28 | "autoprefixer": "^10.4.13",
29 | "chalk": "^5.2.0",
30 | "configstore": "^2.1.0",
31 | "express": "^4.14.0",
32 | "front-matter": "^2.1.1",
33 | "fs-extra": "^1.0.0",
34 | "fs-promise": "^1.0.0",
35 | "handlebars": "^4.0.6",
36 | "klaw-sync": "^6.0.0",
37 | "liquid-to-handlebars": "^0.3.2",
38 | "marked": "^0.3.6",
39 | "moment": "^2.17.1",
40 | "mz": "^2.5.0",
41 | "node-watch": "^0.4.1",
42 | "open": "0.0.5",
43 | "postcss": "^8.4.21",
44 | "prompt-sync": "^4.1.4",
45 | "q": "^1.4.1",
46 | "request": "^2.79.0",
47 | "request-promise": "^4.1.1",
48 | "semver": "^5.3.0",
49 | "shelljs": "^0.7.5",
50 | "string": "^3.3.3",
51 | "stylus": "^0.54.5",
52 | "tailwindcss": "^3.2.7",
53 | "upath": "^2.0.1",
54 | "uuid": "^9.0.0",
55 | "yaml": "^0.3.0",
56 | "yamljs": "^0.2.8",
57 | "yargs": "^17.6.2"
58 | },
59 | "devDependencies": {
60 | "@types/node": "^18.11.18",
61 | "@typescript-eslint/eslint-plugin": "^5.48.1",
62 | "@typescript-eslint/parser": "^5.48.1",
63 | "babel-eslint": "^6.1.2",
64 | "chai": "^3.5.0",
65 | "eslint": "^8.31.0",
66 | "eslint-config-prettier": "^8.6.0",
67 | "eslint-plugin-prettier": "^4.2.1",
68 | "mocha": "^3.1.2",
69 | "mocha-eslint": "^3.0.1",
70 | "pkg": "^5.8.0",
71 | "pre-commit": "^1.2.2",
72 | "prettier": "2.8.2",
73 | "typescript": "^4.9.4"
74 | }
75 | }
76 |
--------------------------------------------------------------------------------
/public/CNAME:
--------------------------------------------------------------------------------
1 | gloriajs.github.io
--------------------------------------------------------------------------------
/public/logo.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/gloriajs/gloria/be7d9cbce14c36a191047f4cc0a0622a6b86e19a/public/logo.png
--------------------------------------------------------------------------------
/test/commands/build.spec.js:
--------------------------------------------------------------------------------
1 | var should = require('chai').should();
2 | var expect = require('chai').expect;
3 |
4 | var build = require('../../lib/commands/build');
5 |
6 | describe('build command is a valid module', function () {
7 | it('will always pass unless build is undefined', function () {});
8 | });
9 |
10 | describe('build will refuse certain arguments', function () {
11 | it('will refuse to build if a parent directory is used as destination', function () {
12 | // var attempt = build.handler({ dest: '../' });
13 | // expect(attempt).to.equal(null);
14 | });
15 | });
16 |
--------------------------------------------------------------------------------
/test/commands/index.spec.js:
--------------------------------------------------------------------------------
1 | var should = require('chai').should();
2 | var gloria = require('../../bin/gloria');
3 |
4 | describe('version', function () {});
5 |
--------------------------------------------------------------------------------
/test/commands/init.spec.js:
--------------------------------------------------------------------------------
1 | // Look into testing user input
2 | var should = require('chai').should;
3 | var expect = require('chai').expect;
4 | var assert = require('chai').assert;
5 |
6 | var init = require('../../lib/commands/init');
7 | var fs = require(`../../lib/utils/fs`);
8 | var Project = require('../../lib/core/project');
9 |
10 | const name = 'sample';
11 |
12 | describe('init command is a valid module', function () {
13 | it('will always pass unless init is undefined', function () {
14 | expect(true).to.equal(true);
15 | });
16 | });
17 |
18 | describe('will run git init with a sample folder', function () {
19 | it(`will create a new project in the specified folder`, function () {
20 | fs.rmdir(name);
21 | init.handler({ name: name, interactive: false, force: true });
22 | const dir = fs.statSync(name);
23 | expect(dir.isDirectory()).to.equal(true);
24 | });
25 | });
26 |
27 | describe('init', function () {
28 | describe('optionsBuilder', function () {
29 | it('should exist', function () {
30 | assert.isFunction(init.optionsBuilder);
31 | });
32 | it('should have valid keys', function () {
33 | const option = { name: '' };
34 | expect(init.optionsBuilder(option)).to.have.all.keys(
35 | 'name',
36 | 'location',
37 | 'longname',
38 | 'layout',
39 | 'author',
40 | );
41 | });
42 | });
43 | describe('showSiteInfo', function () {
44 | it('should exist', function () {
45 | assert.isFunction(init.showSiteInfo);
46 | });
47 | });
48 | describe('confirmSite', function () {
49 | it('should exist', function () {
50 | assert.isFunction(init.confirmSite);
51 | });
52 | it('should return true on default', function () {
53 | const interact = { interactive: false };
54 | assert.equal(init.confirmSite(interact), true);
55 | });
56 | });
57 | describe('inputNameAndLocation', function () {
58 | let argv = { interactive: true };
59 | let project = new Project();
60 | let options = {
61 | name: 'test',
62 | location: 'test',
63 | };
64 | it('should exist', function () {
65 | assert.isFunction(init.inputNameAndLocation);
66 | });
67 | it('should return true if argv input', function () {
68 | assert.equal(init.inputNameAndLocation(argv, project, options), true);
69 | });
70 | it('should return false if argv.interactive false', function () {
71 | argv = { interactive: false };
72 | options = { name: '', locations: '' };
73 | project = new Project(options);
74 | assert.equal(init.inputNameAndLocation(argv, project, options), false);
75 | });
76 | });
77 | describe('inputRest', function () {
78 | const argv = { interactive: true, name: 'test' };
79 | let project = new Project();
80 | const options = {
81 | author: 'test',
82 | longname: 'test',
83 | description: 'test',
84 | layout: 'bootstrap',
85 | };
86 | it('should exist', function () {
87 | assert.isFunction(init.inputRest);
88 | });
89 | it('should return true with all input', function () {
90 | assert.equal(init.inputRest(argv, project, options), true);
91 | });
92 | it('project.config should have all keys', function () {
93 | Object.assign(options, init.optionsBuilder(argv), { author: 'test' });
94 | project = new Project(options);
95 | init.inputNameAndLocation(argv, project, options);
96 | init.inputRest(argv, project, options);
97 | expect(project.config).to.have.all.keys(
98 | 'name',
99 | 'location',
100 | 'longname',
101 | 'layout',
102 | 'author',
103 | 'description',
104 | 'version',
105 | );
106 | });
107 | });
108 | describe('fileLocationNotAvailable', function () {
109 | it('should exist', function () {
110 | assert.isFunction(init.fileLocationNotAvailable);
111 | });
112 | });
113 | describe('createSite', function () {
114 | it('should exist', function () {
115 | assert.isFunction(init.createSite);
116 | });
117 | it('should throw error with empty project', function () {
118 | const project = new Project();
119 | expect(() => init.createSite(project)).to.throw(/invalid location/);
120 | });
121 | });
122 | });
123 |
--------------------------------------------------------------------------------
/test/commands/new.spec.js:
--------------------------------------------------------------------------------
1 | const should = require('chai').should;
2 | const expect = require('chai').expect;
3 | const path = require('path');
4 |
5 | const command = require('../../lib/commands/new');
6 | const fs = require(`../../lib/utils/fs`);
7 |
8 | const options = {
9 | title: 'hello World',
10 | folder: 'sample',
11 | verbose: false,
12 | type: 'post',
13 | category: 'sample',
14 | };
15 |
16 | describe('New command is a valid module', function () {
17 | it('will always pass unless command `new` is undefined', function () {
18 | expect(command).to.be.ok;
19 | });
20 | });
21 |
22 | describe('will run gloria new with sample options to create sample files:', function () {
23 | const result = command.handler(options);
24 | it(`will ensure the destination folder exists`, function () {
25 | const dir = fs.statSync(result.path);
26 | expect(dir.isDirectory()).to.equal(true);
27 | });
28 |
29 | it(`will ensure the destination file exists`, function () {
30 | const file = fs.statSync(result.path);
31 | expect(file).to.be.ok;
32 | });
33 | });
34 |
--------------------------------------------------------------------------------
/test/commands/serve.spec.js:
--------------------------------------------------------------------------------
1 | var should = require('chai').should();
2 | var expect = require('chai').expect;
3 | var assert = require('chai').assert;
4 |
5 | var serve = require('../../lib/commands/serve');
6 | var Project = require('../../lib/core/project');
7 |
8 | describe('serve command is a valid module', function () {
9 | it('will always pass unless serve is undefined', () => null);
10 | });
11 |
12 | describe('Serve', function () {
13 | describe('serveAndBuildSite', function () {
14 | it('exists', function () {
15 | assert.isFunction(serve.serveAndBuildSite);
16 | });
17 | });
18 | describe('initializeSite', function () {
19 | it('exists', function () {
20 | assert.isFunction(serve.initializeSite);
21 | });
22 | it('returns a function', function () {
23 | const dest = '';
24 | assert.isFunction(serve.initializeSite(dest));
25 | });
26 | });
27 | describe('serveStaticAssets', function () {
28 | it('exists', function () {
29 | assert.isFunction(serve.serveStaticAssets);
30 | });
31 | });
32 | describe('setRoutes', function () {
33 | it('exists', function () {
34 | assert.isFunction(serve.setRoutes);
35 | });
36 | });
37 | describe('watchSourceFiles', function () {
38 | it('exists', function () {
39 | assert.isFunction(serve.watchSourceFiles);
40 | });
41 | });
42 | describe('launchSite', function () {
43 | it('exists', function () {
44 | assert.isFunction(serve.launchSite);
45 | });
46 | });
47 | });
48 |
--------------------------------------------------------------------------------
/test/test.spec.js:
--------------------------------------------------------------------------------
1 | const gloria = require(`../bin/gloria`);
2 | const expect = require(`chai`).expect;
3 | const lint = require(`mocha-eslint`);
4 |
5 | lint([`lib/`, 'test/'], {});
6 |
7 | describe('gloria is not undefined', function () {
8 | it('will always pass unless gloria is undefined', function () {
9 | expect(gloria).to.not.equal(undefined);
10 | });
11 | });
12 |
13 | require('./commands/index.spec');
14 | require('./commands/init.spec');
15 | require('./commands/new.spec');
16 | require('./commands/build.spec');
17 | require('./commands/serve.spec');
18 | require('./utils/logger.spec');
19 |
--------------------------------------------------------------------------------
/test/utils/logger.spec.js:
--------------------------------------------------------------------------------
1 | const expect = require('chai').expect;
2 | const $log = require('../../lib/utils/logger');
3 |
4 | describe('$log', () => {
5 | it('should report back when log function is called on mock logger', () => {
6 | expect(typeof $log.log).to.equal('function');
7 | });
8 |
9 | it('should report back when info function is called on mock logger', () => {
10 | expect(typeof $log.info).to.equal('function');
11 | });
12 |
13 | it('should report back when error function is called on mock logger', () => {
14 | expect(typeof $log.error).to.equal('function');
15 | });
16 | });
17 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "target": "ES2015",
4 | "module": "CommonJS",
5 | "strict": true,
6 | "esModuleInterop": true,
7 | "resolveJsonModule": true,
8 | "moduleResolution": "node"
9 | },
10 | "include": ["cli.ts"]
11 | }
12 |
--------------------------------------------------------------------------------