{"id":17384338,"url":"https://github.com/tsertkov/m9-generator","last_synced_at":"2026-03-17T19:44:07.222Z","repository":{"id":57168056,"uuid":"111158825","full_name":"tsertkov/m9-generator","owner":"tsertkov","description":"m9 - Static Site Generator","archived":false,"fork":false,"pushed_at":"2020-04-27T11:37:12.000Z","size":1456,"stargazers_count":5,"open_issues_count":4,"forks_count":1,"subscribers_count":1,"default_branch":"master","last_synced_at":"2025-09-12T20:28:44.961Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/tsertkov.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2017-11-17T23:02:47.000Z","updated_at":"2023-05-09T09:13:22.000Z","dependencies_parsed_at":"2022-09-12T19:12:16.488Z","dependency_job_id":null,"html_url":"https://github.com/tsertkov/m9-generator","commit_stats":null,"previous_names":[],"tags_count":34,"template":false,"template_full_name":null,"purl":"pkg:github/tsertkov/m9-generator","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsertkov%2Fm9-generator","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsertkov%2Fm9-generator/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsertkov%2Fm9-generator/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsertkov%2Fm9-generator/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/tsertkov","download_url":"https://codeload.github.com/tsertkov/m9-generator/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/tsertkov%2Fm9-generator/sbom","scorecard":{"id":900700,"data":{"date":"2025-08-11","repo":{"name":"github.com/tsertkov/m9-generator","commit":"504ee2502220cdbe0cc4146ca58005a8cf8610c7"},"scorecard":{"version":"v5.2.1-40-gf6ed084d","commit":"f6ed084d17c9236477efd66e5b258b9d4cc7b389"},"score":3.5,"checks":[{"name":"Token-Permissions","score":-1,"reason":"No tokens found","details":null,"documentation":{"short":"Determines if the project's workflows follow the principle of least privilege.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#token-permissions"}},{"name":"Maintained","score":0,"reason":"0 commit(s) and 0 issue activity found in the last 90 days -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project is \"actively maintained\".","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#maintained"}},{"name":"Packaging","score":-1,"reason":"packaging workflow not detected","details":["Warn: no GitHub/GitLab publishing workflow detected."],"documentation":{"short":"Determines if the project is published as a package that others can easily download, install, easily update, and uninstall.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#packaging"}},{"name":"Dangerous-Workflow","score":-1,"reason":"no workflows found","details":null,"documentation":{"short":"Determines if the project's GitHub Action workflows avoid dangerous patterns.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#dangerous-workflow"}},{"name":"Code-Review","score":0,"reason":"Found 0/30 approved changesets -- score normalized to 0","details":null,"documentation":{"short":"Determines if the project requires human code review before pull requests (aka merge requests) are merged.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#code-review"}},{"name":"Binary-Artifacts","score":10,"reason":"no binaries found in the repo","details":null,"documentation":{"short":"Determines if the project has generated executable (binary) artifacts in the source repository.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#binary-artifacts"}},{"name":"Pinned-Dependencies","score":-1,"reason":"no dependencies found","details":null,"documentation":{"short":"Determines if the project has declared and pinned the dependencies of its build process.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#pinned-dependencies"}},{"name":"CII-Best-Practices","score":0,"reason":"no effort to earn an OpenSSF best practices badge detected","details":null,"documentation":{"short":"Determines if the project has an OpenSSF (formerly CII) Best Practices Badge.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#cii-best-practices"}},{"name":"Security-Policy","score":0,"reason":"security policy file not detected","details":["Warn: no security policy file detected","Warn: no security file to analyze","Warn: no security file to analyze","Warn: no security file to analyze"],"documentation":{"short":"Determines if the project has published a security policy.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#security-policy"}},{"name":"Vulnerabilities","score":10,"reason":"0 existing vulnerabilities detected","details":null,"documentation":{"short":"Determines if the project has open, known unfixed vulnerabilities.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#vulnerabilities"}},{"name":"Fuzzing","score":0,"reason":"project is not fuzzed","details":["Warn: no fuzzer integrations found"],"documentation":{"short":"Determines if the project uses fuzzing.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#fuzzing"}},{"name":"License","score":10,"reason":"license file detected","details":["Info: project has a license file: LICENSE:0","Info: FSF or OSI recognized license: MIT License: LICENSE:0"],"documentation":{"short":"Determines if the project has defined a license.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#license"}},{"name":"Signed-Releases","score":-1,"reason":"no releases found","details":null,"documentation":{"short":"Determines if the project cryptographically signs release artifacts.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#signed-releases"}},{"name":"Branch-Protection","score":-1,"reason":"internal error: error during branchesHandler.setup: internal error: githubv4.Query: Resource not accessible by integration","details":null,"documentation":{"short":"Determines if the default and release branches are protected with GitHub's branch protection settings.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#branch-protection"}},{"name":"SAST","score":0,"reason":"SAST tool is not run on all commits -- score normalized to 0","details":["Warn: 0 commits out of 8 are checked with a SAST tool"],"documentation":{"short":"Determines if the project uses static code analysis.","url":"https://github.com/ossf/scorecard/blob/f6ed084d17c9236477efd66e5b258b9d4cc7b389/docs/checks.md#sast"}}]},"last_synced_at":"2025-08-24T15:28:11.592Z","repository_id":57168056,"created_at":"2025-08-24T15:28:11.592Z","updated_at":"2025-08-24T15:28:11.592Z"},"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30629877,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-17T17:32:55.572Z","status":"ssl_error","status_checked_at":"2026-03-17T17:32:38.732Z","response_time":56,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-16T07:45:12.457Z","updated_at":"2026-03-17T19:44:07.198Z","avatar_url":"https://github.com/tsertkov.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# m9-generator\n\n\u003e Static site generator powered by `webpack` bundling, `handlebars` templating and `browser-sync` live-reloading.\n\n## Table of Contents\n\n* [Overview](#overview)\n* [Installation](#installation)\n  + [Yarn tasks as package.json scripts](#yarn-tasks-as-packagejson-scripts)\n* [CLI interface](#cli-interface)\n* [API interface](#api-interface)\n* [Development mode](#development-mode)\n* [Source and Destination directories](#source-and-destination-directories)\n* [Stages](#stages)\n* [Entrypoint files](#entrypoint-files)\n* [Target browsers](#target-browsers)\n* [Babel](#babel)\n* [Handlebars](#handlebars)\n  + [Partials](#partials)\n  + [Layouts](#layouts)\n  + [Helpers](#helpers)\n  + [Embedded handlebars-helpers](#embedded-handlebars-helpers)\n* [Webpack](#webpack)\n  + [Development and Production modes](#development-and-production-modes)\n  + [Handlebars loader](#handlebars-loader)\n  + [Babel loader](#babel-loader)\n  + [PostCSS loader](#postcss-loader)\n  + [Visualizer](#visualizer)\n* [Metalsmith](#metalsmith)\n  + [Configuration](#configuration)\n  + [Front-matter](#front-matter)\n  + [Handlebars template data context](#handlebars-template-data-context)\n    - [System data](#system-data)\n    - [Static content](#static-content)\n    - [Dynamic content](#dynamic-content)\n  + [Public directory](#public-directory)\n* [Developing m9-generator](#developing-m9-generator)\n\n## Overview\n\nm9-generator is static site generator relying on `handlebars` for static files templating, `webpack` for JavaScript and CSS bundling and `browser-sync` for local live-reload development server.\n\nm9-generator follows future-focused approach and provides support to new standard solutions when handling CSS and JavaScript.\n\n- JavaScript: [Stage3](https://babeljs.io/docs/en/babel-preset-stage-3)\n- CSS: [Stage2, nesting-rules, custom-media-queries](https://preset-env.cssdb.org/features)\n\n## Installation\n\n\u003e NB! Use `yarn` instead of `npm`. m9-generator depends on `peerDependencies` in `package.json` and so far only `yarn` installs them.\n\n\u003e NB! It is assumed that per-site directory layout with package.json is used in this documentation examples. Use `yarn init` to create one.\n\nInstall `@tsertkov/m9-generator` as your local depencency:\n\n```sh\n$ yarn add @tsertkov/m9-generator\n```\n\n### Yarn tasks as package.json scripts\n\nAdd following `scripts` in your site `package.json`:\n\n```json\n\"scripts\": {\n  \"m9\": \"m9\",\n  \"test\": \"m9 test\",\n  \"dev\": \"m9 dev\",\n  \"build\": \"m9 build\"\n}\n```\n\nTo run m9-generator tasks with `yarn` locally\n\n```sh\n$ yarn test\n$ yarn run dev\n$ yarn run build\n$ yarn run m9\n$ yarn run m9 \u003ctask\u003e\n```\n\n## CLI interface\n\nm9-generator comes with `m9` command line tool to execute `gulp` tasks.\n\nRun `yarn run m9` to execute default help task printing complete list of available tasks to run with `yarn run m9 \u003ctask\u003e`.\n\nThere are tasks and sub-tasks in m9-generator. Tasks are always a groups of sub-tasks.\n\nMain tasks are:\n- `build`: build static site files into destination directory\n- `dev`: continuously build and live-reload site with local development server\n- `test`: test site\n\nRun `./node_modules/.bin/m9` to see default help screen:\n\n```\nMain Tasks\n------------------------------\n    build\n    dev\n    help\n    test\n\nSub Tasks\n------------------------------\n    build-clean\n    build-copy\n    build-metalsmith\n    build-webpack\n    dev-browsersync\n    dev-watch\n    test-standard\n```\n\nRun `./node_modules/.bin/m9 --tasks` to see complete task tree generated by `gulp`.\n\n```\n[13:23:38] ├── build-clean\n[13:23:38] ├── build-copy\n[13:23:38] ├── build-metalsmith\n[13:23:38] ├── build-webpack\n[13:23:38] ├── dev-browsersync\n[13:23:38] ├── dev-watch\n[13:23:38] ├── help\n[13:23:38] ├── test-standard\n[13:23:38] ├─┬ build\n[13:23:38] │ └─┬ \u003cseries\u003e\n[13:23:38] │   ├── build-clean\n[13:23:38] │   ├── build-copy\n[13:23:38] │   ├── build-webpack\n[13:23:38] │   └── build-metalsmith\n[13:23:38] ├─┬ dev\n[13:23:38] │ └─┬ \u003cseries\u003e\n[13:23:38] │   ├── build-clean\n[13:23:38] │   ├── build-copy\n[13:23:38] │   ├── dev-browsersync\n[13:23:38] │   ├── build-metalsmith\n[13:23:38] │   └── dev-watch\n[13:23:38] ├─┬ test\n[13:23:38] │ └─┬ \u003cseries\u003e\n[13:23:38] │   └── test-standard\n[13:23:38] └─┬ default\n[13:23:38]   └─┬ \u003cseries\u003e\n[13:23:38]     └── help\n```\n\n## API interface\n\nm9-generator exports preconfigured `gulp` instance. See [gulp API docs](https://github.com/gulpjs/gulp/blob/master/docs/API.md) for details.\n\nExample using m9-generator API:\n\n```javascript\nconst m9 = require('@tsertkov/m9-generator')()\nm9.series('build')(err =\u003e {\n  if (err) {\n    console.log('error happened', err)\n  } else {\n    console.log('build done')\n  }\n})\n```\n\n## Development mode\n\n`yarn run dev` executes BrowserSync serving static files from build directory connected to webpack-dev-middleware for dynamic asset serving.\n\nLook for BrowserSync details in output:\n\n```\n[Browsersync] Access URLs:\n --------------------------------------\n       Local: https://localhost:3000\n    External: https://192.168.0.1:3000\n --------------------------------------\n          UI: http://localhost:3001\n UI External: http://192.168.0.1:3001\n --------------------------------------\n[Browsersync] Serving files from: /Users/example/Projects/example.com/build\n```\n\nUpdates to most of the `src/**/*` files must trigger recompilation and live-reload updated pages opened in browsers.\n\nRun `yarn run test` to execute automated tests for current site.\n\n## Source and Destination directories\n\n`m9` works by reading input from files in source directory, builds site, then persists files in destination directory. By default source and destination directories are `./src` and `./build` accordingly.\n\nDirectories can be set with command line argument `--src=\u003cSRC\u003e`, `--dst=\u003cDST\u003e` or environment variable `SRC` and `DST`.\n\n## Stages\n\nStage defaults to `development` and defines environment current site is building for.\n\nStage can be set with command line argument `--stage` or `STAGE` environment variable:\n\n- `STAGE=staging yarn run build`\n- `yarn run build --stage=staging`\n\n## Entrypoint files\n\nm9-generator uses `metalsmith` to build static files and `webpack` to bundle JavaScript and CSS. Both are relying on entrypoint files as their input.\n\nEach file matched by `\u003cSRC\u003e/pages/**/*.hbs` pattern is processed by `metalsmith` resulting in one or many files build in destination directory. All other files in `\u003cSRC\u003e/pages/**/*` are copied as is.\n\nEach file matched by `\u003cSRC\u003e/scripts/*.js|.css` pattern is bundled in corresponding JS or CSS file in `\u003cDST\u003e/assets` directory.\n\n## Target browsers\n\nWebpack loaders for JavaScript and CSS are respecting configured target browsers defined in [`.browserslistrc`](https://www.npmjs.com/package/browserslist) file.\n\n\u003e NB! The most pragmatic browserlist query is used by default when `.browserslistrc` file is not given.\n\n## Babel\n\nTwo different babel transformation processes are running in m9-generator. One is done by `@babel/register` transpiling files on the fly for current node version using require hook. Another by webpack bundling client-side package with `babel-loader`.\n\nBoth transformations use the same presets `@babel/preset-stage-3` and `@babel/preset-env`, but differ in targets for `preset-env`.\n\n## Handlebars\n\nHandlebars is simple, powerful and extendable templating system. Custom Partials and Helpers allow building reusable components with ease. Read more on https://handlebarsjs.com/.\n\n### Partials\n\nBy default handlebars partials are loaded from `\u003cSRC\u003e/partials/*.hbs`. Read more about handlebars partials on https://handlebarsjs.com/partials.html.\n\n### Layouts\n\nLayouts are implemented by native handlebars [partial blocks](https://handlebarsjs.com/partials.html#partial-block).\n\nLayout example `\u003cSRC\u003e/partials/layout-default.hbs`:\n\n```handlebars\n\u003c!DOCTYPE html\u003e\n\u003chtml lang=\"en\"\u003e\n\u003chead\u003e\n  \u003cmeta charset=\"utf-8\"\u003e\n  \u003ctitle\u003e{{title}}\u003c/title\u003e\n  \u003cmeta name=\"viewport\" content=\"width=device-width, initial-scale=1\"\u003e\n  \u003clink rel=\"stylesheet\" href=\"{{__assets.[styles.css]}}\"\u003e\n\u003c/head\u003e\n\u003cbody\u003e\n  {{\u003e header}}\n  \u003csection class=\"content\"\u003e\n    {{\u003e @partial-block}}\n  \u003c/section\u003e\n  {{\u003e footer}}\n  \u003cscript src=\"{{__assets.[scripts.js]}}\"\u003e\u003c/script\u003e\n\u003c/body\u003e\n\u003c/html\u003e\n```\n\nPage template example `\u003cSRC\u003e/pages/index.html.hbs`:\n\n```handlebars\n---\ntitle = \"{{site.title}}\"\n---\n{{#\u003e layout-default}}\n\u003carticle\u003e\n  \u003cdiv class=\"body\"\u003e\n    {{{readme.content}}}\n  \u003c/div\u003e\n\u003c/article\u003e\n{{/layout-default}}\n```\n\n### Helpers\n\nHelpers are loaded with `\u003cSRC\u003e/helpers/*.js` pattern.\n\nThese are native JavaScript files and must use `module.exports = (content) =\u003e 'output string'` format to enable dynamic loading.\n\n### Embedded handlebars-helpers\n\n188 helpers from [`handlebars-helpers`](https://github.com/helpers/handlebars-helpers) are available to handlebars engine. Before writing your own first consider searching similar in [handlebars-helpers](https://github.com/helpers/handlebars-helpers).\n\n## Webpack\n\nWebpack searches for entry points in `\u003cSRC\u003e/scripts/*.js` and `\u003cSRC\u003e/styles/*.css` and writes files in `\u003cDST\u003e/assets/`.\n\nThere is `\u003cDST\u003e/assets/manifest.json` file with assets manifest pre-loaded into templating context as `__assets` variable.\n\n### Development and Production modes\n\n`development` mode is enabled when `stage === 'development'` is true. Otherwise `production` mode is active. Different optimizations and loader settings are enabled based on active mode.\n\n### Handlebars loader\n\n`*.hbs` files are converted to compiled handlebars template functions including resolved helpers, partials and runtime automatically.\n\n```JavaScript\nimport itemCard from '../../partials/item-card.hbs'\n\n// ..\nconst item = { key: \"value\" }\nconst html = itemCard(item)\n// ..\n\n```\n\nRead more about `handlebars-loader` at https://github.com/pcardune/handlebars-loader#readme.\n\n### Babel loader\n\n`*.js` files are processed by babel using browsers target defined in `.browserslistrc` or default one for browsers.\n\nReam more about `babel-loader` at https://github.com/babel/babel-loader#readme.\n\n### PostCSS loader\n\n`*.css` files are processed by PostCSS with `postcss-preset-env`.\n\nRead more about `postcss-loader` at https://github.com/postcss/postcss-loader#readme.\n\n### Visualizer\n\nIn `development` mode [webpack-visualizer-plugin](https://github.com/chrisbateman/webpack-visualizer#readme) is enabled and results are avalable at `https://localhost:3000/webpack-visualizer/` or other domain/port you use locally.\n\n## Metalsmith\n\nMetalsmith compiles static files found in `\u003cSRC\u003e/pages/` into `\u003cDST\u003e/` directory. It extracts front-matter headers and passes `*.hbs` files through Handlebars templating system.\n\n### Configuration\n\nm9-generator builds single configuration object internally. This object can be modified per site by exporting configuration function in `\u003cSRC\u003e/config.js`.\n\nStage aware configuration file example:\n\n```JavaScript\nconst config = {\n  default: {\n    apiEndpoint: 'https://staging-api.example.com'\n  },\n  development: {\n    site_url: 'https://localhost:3000'\n  },\n  staging: {\n    site_url: 'https://staging.example.com'\n  },\n  production: {\n    site_url: 'https://example.com',\n    apiEndpoint: 'https://api.example.com'\n  }\n}\n\nmodule.exports = m9config =\u003e Object.assign(\n  m9config,\n  Object.assign(\n    {},\n    config.default,\n    config[m9config.stage]\n  )\n)\n```\n\n### Front-matter\n\nMetalsmith is parsing front-matter headers in template files using `---` as delimiter. Front-matter content must be in TOML format. It is used to control metalsmith plugins and overwrite data context variables.\n\n### Data context\n\nm9-generator preloads data context and exponses it to Handlebars templating. There are two types of input data: **static content** in form of json files and **dynamic content** as JavaScript files.\n\n#### System data\n\nSystem variables avaialble in template context are prefixed with `__`.\n\n| Variable name | Description |\n| --- | --- |\n| `__assets` | Webpack assets manifest: `asset-name -\u003e asset-filename` |\n| `__config` | Configuration object |\n\n#### Static content by `json-dir` content plugin\n\nStatic content is loaded from JSON files found in `\u003cSRC\u003e/content/static` directory.\n\nFor example following static content files in `content/static` directory:\n\n```javascript\n// content/static/user.json\n{\n  [\n    \"name\": \"user1\",\n    \"email\": \"user1@example.com\",\n    \"group\": [{\n      \"ID\": 1\n    }\n  ],\n  [\n    \"name\": \"user2\",\n    \"email\": \"user2@example.com\",\n    \"group\": [{\n      \"ID\": 2\n    }\n  ]\n}\n\n// content/static/group.json\n{\n  [\n    \"id\": 1,\n    \"name\": \"group1\"\n  ],\n  [\n    \"id\": 2,\n    \"name\": \"group2\"\n  ]\n}\n\n// content/static/site.json\n{\n  \"adminEmail\": \"admin@example.com\",\n  \"title\": \"site title\",\n  \"description\": \"site description\"\n}\n```\n\nThis content is accessible in handlebars templates as variables: `user`, `group` and `site` accordingly to JSON file names.\n\n```handlebars\nsite title:                                 {{site.title}}\nname property of first user in collection:  {{user.[0].name}}\nname property of first group in collection: {{group.[0].name}}\nfirst user group name:                      {{user.[0].group.[0].name}}\nsecond user group name:                     {{user.[1].group.[0].name}}\n```\n\n#### Dynamic content by `js-dir` content plugin\n\nIt is possible to define dynamic (programmable) content and expose it to template data. Each JavaScript file in `\u003cSRC\u003e/content/dynamic` must follow pattern `\u003ccontent-type\u003e.js` and export function of a form:\n\n```javascript\n\nmodule.exports = function (proto, content, config) {\n  // do something with the proto (it is actual entity obj in fact)\n  Object.defineProperty(proto, 'content', {\n    configurable: true,\n    enumerable: true,\n    get: () =\u003e {\n      // return smth\n      return 'empty value'\n    }\n  })\n\n  return proto\n}\n```\n\nAugmenting initial content loaded from static JSON files with dynamic functions is a common integration method. In cases when static content of the same content type is not found new empty object is passed as `proto` to a content function.\n\n### Public directory\n\nAll files and folders from `\u003cSRC\u003e/public/` directory are copied to destination `\u003cDST\u003e` directory. This is a good place to put static image files, favicons, etc.\n\n## Custom gulp tasks\n\nGulp tasks found matching `\u003cSRC\u003e/tasks/*.js` are automatically registrered and available via m9 cli.\n\nFor example `\u003cSRC\u003e/tasks/custom-task.js` to define `custom-task`:\n```javascript\nmodule.exports = async function customTask (config) {\n  console.log('Custom task test')\n}\n```\n\n## Developing m9-generator\n\n### JavaScript transpilation\n\nJavaScript sources of m9-generator are transpiled with babel. Sources are located in `src/` directory. Compiled files are placed in `dist/`.\n\nRun `npm run build-dist` to compile files in `dist/`.\n\nBy default m9 runs from `dist/` directory. To run from `src/` during development pass `--m9-use-src` command line argument.\n\n### Embedded documentation site\n\nDocumentation site is embedded with m9-generator. Its source directory is `docs-src/` and destination `docs/`.\n\nRun `npm run dev` to start development server for documentation site.\n\n### Using `npm link`\n\nRun `npm link` inside m9-generator local repo to link it from global node_modules. To link dev version of m9-generator in site project run `npm link @tsertkov/m9-generator`.\n\nFor example assume having `~/m9-generator` and `~/my-example-site` with generator repo and site repo accordingly. Then to run example-site using m9-generator from `~/m9-generator` do the following:\n\n```sh\n$ cd ~/m9-generator\n$ npm link\n$ cd ~/my-example-site\n$ npm link @tsertkov/m9-generator\n$ npm run dev --m9-use-src\n```\n\n### Publish new npm version\n\nUse `npm version` command to publish new version of npm package and push new git tag to remote.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsertkov%2Fm9-generator","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ftsertkov%2Fm9-generator","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ftsertkov%2Fm9-generator/lists"}