{"id":18925163,"url":"https://github.com/exadel-inc/server-sketch","last_synced_at":"2026-03-07T21:32:30.128Z","repository":{"id":48215968,"uuid":"314007256","full_name":"exadel-inc/server-sketch","owner":"exadel-inc","description":"Component based server for frontend developers. Helps quickly serve static files, generate html for components \u0026 pages for new projects","archived":false,"fork":false,"pushed_at":"2025-09-26T15:27:42.000Z","size":212,"stargazers_count":15,"open_issues_count":4,"forks_count":0,"subscribers_count":4,"default_branch":"main","last_synced_at":"2025-09-26T17:33:40.675Z","etag":null,"topics":["express","expressjs","frontend-developers","javascript","js","nodejs","server","server-sketch"],"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/exadel-inc.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,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2020-11-18T17:25:01.000Z","updated_at":"2025-09-26T15:22:36.000Z","dependencies_parsed_at":"2025-04-15T12:50:47.257Z","dependency_job_id":null,"html_url":"https://github.com/exadel-inc/server-sketch","commit_stats":null,"previous_names":[],"tags_count":1,"template":false,"template_full_name":null,"purl":"pkg:github/exadel-inc/server-sketch","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exadel-inc%2Fserver-sketch","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exadel-inc%2Fserver-sketch/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exadel-inc%2Fserver-sketch/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exadel-inc%2Fserver-sketch/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/exadel-inc","download_url":"https://codeload.github.com/exadel-inc/server-sketch/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/exadel-inc%2Fserver-sketch/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":30231636,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-03-07T19:01:10.287Z","status":"ssl_error","status_checked_at":"2026-03-07T18:59:58.103Z","response_time":53,"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":["express","expressjs","frontend-developers","javascript","js","nodejs","server","server-sketch"],"created_at":"2024-11-08T11:09:49.071Z","updated_at":"2026-03-07T21:32:30.108Z","avatar_url":"https://github.com/exadel-inc.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"![](docs/images/icon.png)\n\n# @exadel/server-sketch\n\n\n![](docs/images/84xLDr-small.png)\n\n## Overview\nFast, flexible and extensible web-server (🌐) for [Component-Based](#components) architecture/applications 💻. Best for local development and quickly provide POC for business(🧰). Based on [express.js](http://expressjs.com/) web-server.\n\nHas possibility to use by front developers as a part of AEM ([Adobe Experience Manager (TM)](https://www.adobe.com/marketing/experience-manager.html)) development process.\n\nEasy way to quickly [setup](#configuration) and [start server](#how-to-use) for frontend developers(🐜), who doesn't want to spend a lot of time to do it ⌚. \n\n\n## How to use\n### Install\n1) Install **[NodeJs](https://nodejs.org/en/)** (This will also install **npm**)\n2) Create simple *package.json* (`` npm init ``)\n3) Install [@exadel/server-sketch](https://www.npmjs.com/package/@exadel/server-sketch) as dependency for your project (👍):\n```shell script\nnpm i @exadel/server-sketch --save-dev\n```\n4) And CLI [@exadel/server-sketch-cli](https://www.npmjs.com/package/@exadel/server-sketch-cli) (it helps to create demo project) _[optional]_:\n```shell script\nnpm i @exadel/server-sketch-cli\n```\n\n \n### Quick Start\n1) Create demo project(structure) _[optional]_:\n```shell script\nnpx server-sketch create-demo-project \n```\n\n2) Start server:\n```shell script\nnpm run server\n```\n\n3) Easiest! You can make a ☕ :) \n\n\n### Engine\nProject based on [express.js](http://expressjs.com/) web server with simple, but useful middleware. \n\nTemplate engine for pages: [express-dot-engine](https://www.npmjs.com/package/express-dot-engine) with some improvements.\n\nDefault routing based on [folder/file](#routing-based-on-file-system) structure under 📂: ``${PROJECT_ROOT}/server/views/pages/``.\n\nServer supports **[pages](#page)**, **[layouts](#page-template-structure)**, **inheriting pages** (``layout`` property), **[integrated proxy](#popular-options)**, **[partials](#component-view-template)**, **[static resources](#popular-options)**, **[middleware](#extended-options)**, **[multiprojects](#component-folder-structure)**, **[browserSync](https://browsersync.io/) [integration](#popular-options)** and **[reusable components](#components)**.\n\n\n### Configuration\nConfiguration object should be passed as a first argument into ``.start()`` function (see ``server/start.js``).\n\n#### Popular options\n\n| Param     | Type      | Default Value     | Description |\n|--------   |:------:   |:---------------:  |-------------|\n| ``port``  | *{integer}* | **3000**            | Port for your web-server           |\n| ``openAfterStart``  | *{string / null}* | **http://localhost:\u003c%= port %\u003e/index.html**            | Automatically open URL after web-server will be started |\n| ``optimization``  | *{bool}* | **false**            | Enable optimizations. Cache + gzip + view cache + runtime cache for base router |\n| ``cacheTime``  | *{integer}* | **120**            | Cache time (minutes) |\n| ``public``  | *[string, ...]* | **['static/']**            | List of folders(📂), where you are stored static resources(images, js, css, json etc.). Relative to server root folder. |\n| ``jscssDestFolders``  | *[string, ...]* | **['../.temp/dest/', '../dest/']** | Paths to folders(📂) with generated js \u0026 css files(used with ``browserSync.watchAndReload`` \u0026 ``public`` options). Relative to server root folder. |\n| ``browserSync``  | *{object / null}* | **object** | Enable [BrowserSync](https://browsersync.io/) integration |\n| \u0026nbsp;\u0026nbsp;\u0026nbsp;``browserSync.port``  | *{integer}* | **3001** | BrowserSync port |\n| \u0026nbsp;\u0026nbsp;\u0026nbsp;``browserSync.watchAndReload``  | *[string, ...]* | **[]** | List of folders(📂) for watching. When it will be changed, then pages will be reloaded |\n| ``proxy``  | *{object / null}* | **null** | If you need to use a proxy, you can configure individual requests with the proxies. Usefull to prevent problems with CORS, if you need to have integrating with backend |\n| \u0026nbsp;\u0026nbsp;\u0026nbsp;``proxy.rules``  | *[object, ...]* | **[]** | List of proxies. Object format: [below](#proxy-rules-object-format)  |\n| \u0026nbsp;\u0026nbsp;\u0026nbsp;``proxy.defaultHost``  | *string / null* | **null** | Default host for \"to\" params from proxy config [objects](#proxy-rules-object-format) |\n\n#### Proxy rules object format\n- ``from``: *{string}* | *[required]* URL on you local web-server. Example: '/service/books/all.json'.  \n- ``to``: *{string}* | *[required]* Destination URL for request. Example: 'https://my-backend-api-server.com/service/books/all.json'. (both examples: all requests from '/service/books/all.json' will delegated to 'https://my-backend-api-server.com/service/books/all.json')  \n- ``transform(proxyRes, proxyResData)``: *[optional]* Custom response transformer. Should returns transformed data\n- ``transformHeaders(headers)``: *[optional]* Custom response headers transformer. Should returns transformed headers\n\n\n#### Extended options\n\n| Param     | Type      | Default Value     | Description |\n|--------   |:------:   |:---------------:  |-------------|\n| ``serverRoot``  | *{string}* | **server/**   | Server root folder. |\n| ``baseRouter``  | *{string}* | **null**   | Path to base router. It disables default [file-folder routing](#routing-based-on-file-system). Should exports function. First argument will be express app instance. Path should be relative to server folder |\n| ``doT``  | *{object}* | **{...}**   | [doT.templateSettings](https://olado.github.io/doT/index.html) |\n| ``defaultPageUrl``  | *{string}* | **index.html**   | Default redirection if url doesn't contain \".html\". Used by [file-folder routing](#routing-based-on-file-system) |\n| ``defaultPageRendition``  | *{string}* | **index.html**   | Default page view(rendition) for URLs: /path/to/index.html. You can customize it here: server/views/index.html.  Used by [file-folder routing](#routing-based-on-file-system) |\n| ``views``  | *{string}* | **views/**   |  Path to directory with [views](#page). Relative to server root folder. |\n| ``controllers``  | *{string}* | **controllers/**   |  Path to directory(📂) with [page controllers](#api-page-object). Relative to server root folder. |\n| ``components``  | *{string}* | **src/**   |  Path to directory(📂) with your [components](#components). Relative to project(cwd) root folder. |\n| ``middleware``  | *{string}* | **middleware/*.js**   |  [Middleware for Express](https://expressjs.com/en/guide/writing-middleware.html). Can be array. Js files should export function. Function will retrieve as a first argument: [express app instance](https://expressjs.com/en/4x/api.html) |\n\n\n### Routing based on file system\n![](docs/images/file-routing.png)\n\n### Page\n#### Overview\nAll server pages should be placed under : ``server/views/pages/`` 📂. It can be used as a regular HTML page or can be generated using built-in template engine. \n\n#### Page template structure\nHTML page structure contains 2 sections:\n\n- **Configuration section**: [YAML](https://yaml.org/) at the top. Configuration object(defined between ---) available as ``layout`` object. **Best practice:** use it for global layout configuration(title, body classes, scripts for injection). Default values better to defined in parent page (layout option). Properties are inheritable. **layout** property is reserved and used for showing what is html page to use as a parent.   \n    Example:\n    ```html\n        ---\n        layout: '%%PATH_TO_VIEWS%%/layouts/html5.html'\n        title: My Page\n        bodyClass: my-page\n        myCustomNumbers: [1, 2, 3]\n        ---\n        \n        {{##myCustomBlock: \n            \u003c!-- I will available as \"layout.myCustomBlock\" --\u003e  \n        #}}  \n    \n        {{##body:\n            \u003ch1\u003e {{! layout.title }} \u003c/h1\u003e\n            \u003cdiv class=\"url\"\u003e {{= page.location.path }} \u003c/div\u003e\n            \u003cul class=\"nums\"\u003e\n            {{~ layout.myCustomNumbers :num }}\n                \u003cli\u003e {{! num }} \u003c/li\u003e\n            {{~}}\n            \u003c/ul\u003e\n        #}}\n    ```\n- **Definition Blocks Section**: Section with page blocks. **Best Practice:** Split layout with important blocks(see 📂: ``server/views/layouts/`` files). Each block available as property in ``layout`` global object. Example: for previous code ``layout`` object will contain: ``body``, ``myCustomBlock``, ``title``, ``bodyClass``, ``myCustomNumbers`` properties. Minimum 1 definition block (between ``{{##blockName: #}}``) *required*.\n\nSmall overview about features you can find in **[Templates](#templates)** section [⏬](#templates). \n\n#### API: Page object \n``page`` object provide properties for current page rendition(request). That object available in all pages under ``server/views/`` 📂 and for each component (in template).\n- ``page.renderComponent(componentName, componentOptions)`` : Render [component](#components) by name with options(returns HTML). Example: ``{{= page.renderComponent('demo/hello-world', { data: 'example1' }) }}`` will render component ``src/demo/hello-world/`` with data ``src/demo/hello-world/data-example1.json`` and pass ``{ data: 'example1' }`` as options. \n- ``page.request``: Express [request](https://expressjs.com/en/4x/api.html#req) object\n- ``page.response``: Express [response](https://expressjs.com/en/4x/api.html#res) object\n- ``page.location``: Request url object\n- ``page.getPageTitle(priorityTitle)``: Calculating page title by request url. If ``priorityTitle`` defined, then it will be used. **Best practice**: define ``priorityTitle`` as a yaml property at the top of the page.\n- ``page.resolveURL(url)``: Convert url from absolute to relative for request url. Example: request is: ``/my-project/examples/hi.html``. ``page.resolveURL('/images/log.png')`` will return ``./../../images/log.png``.\n- ``page.invokeController(controllerName, ...args)``: Run node JS file from ``server/controllers/${controllerName}.js``. Controller should export function. ``this`` context will be ``page`` object.\n- ``page.getFile(fileName)``: Read file. Path should be relative to ``server/`` 📂.\n- ``page.initComponent(componentName, componentOptions)``: The same as ``page.renderComponent``, but doesn't generate html(it just create [Component Object](#api-component-object)).\n\n\n#### Page Controller\nPage controller is a js file which exports regular function. JS file should be placed as ``/server/controllers/${controllerName}.js``. Invoke controller you can with [Page API](#api-page-object): ``page.invokeController(controllerName, arg1, arg2, ...)``. ``this`` context in exported function will be **[page object](#api-page-object)**.\n\n**Example 1(run function):**\n```js\n/* /sever/controllers/log.js */\nlet i = 0;\nmodule.exports = function (msg) {\n    // this === page\n    ++i;\n    console.log(`${i}. Page: \"${this.location.pathname}\". Message: \"${msg}\"`);\n}\n``` \n\n```html\n\u003c!-- your html page /server/views/pages/**/*.html --\u003e\n{{\n    page.invokeController('log', 'Some debug message'); \n    page.invokeController('log', 'Another message');\n    // see nodejs console\n}}\n```\n\n**Example 2(extend \"page\" with new functionality)**:\n```js\n/* /sever/controllers/createLogger.js */\nlet i = 0;\nmodule.exports = function () {\n    // this === page\n    this.log = function (msg) {\n        ++i;\n        console.log(`${i}. Page: \"${this.location.pathname}\". Message: \"${msg}\"`);\n    };\n}\n``` \n\n```html\n\u003c!-- your html page /server/views/pages/**/*.html --\u003e\n{{\n    page.invokeController('createLogger'); // we extended page with new method \".log(msg)\" \n    page.log('Some debug message'); \n    page.log('Another message');\n    // see nodejs console\n}}\n```\n\n\n### Components\nAll components should be placed under ``src/`` 📂. **Best practice:** Place all files(related with your component) into component folder. No requirements regarding folder structure, but we recommend to use minimum 2 levels: ``src/${groupName}/${componentName}/``. \nIt will help in the future to customize existing components(example: will be created new version of buttons or product cards or new page will be created or redesign existing components 🦄).\n\n#### Component folder structure\nOnly 1 required file is ``config.json``. It should be placed in your component folder. We recommend to follow next structure:\n```js\n/server\n    start.js \u003c- bootstrap server\n    /static \u003c- folder for static files \n        /images\n            ...\n    /middleware \u003c- folder for middleware \n        ...\n    /views\n        /layouts \u003c- common pages layouts\n            ...\n        /pages \u003c- your pages will be here\n            ...\n/src\n    /${groupName} \u003c- group name it is smthg like a scope\n        /${componentName}\n            /ui \u003c- we recommend put UI resources(js, css(less, scss), images, templates etc. what are using in browser) into \"ui\"  folder\n                ${componentName}.js (optional)\n                ${componentName}.less (.scss) (optional)\n                ${componentName}.hbs (template which is using in browser) (optional)\n                ...\n            config.json \u003c- JSON file with component configuration. Required.\n            data-***.json \u003c- JSON files with predefined data. Helps to prevent copy/paste data between usages. Optional.\n            ${componentName}.html \u003c- Server side component template(component rendition). Any name can be used. See \"config.json \u003e view\" property. \n            ${componentName}Controller.js \u003c- Any server side js logic related with component. Optional.\n        /${componentName2}\n            ...\n```  \n#### config.json\n``config.json`` file reserved properties (see demo project: \"/src/demo/header/config.json\"):\n- ``view``:*(required)* Path to server side component html template.\n- ``controller``:*(optional)* Path to js file with server side logic related with component. File should export object(will be prototype for your component object). ``init()`` function is required, but you can add another methods for you component and then call them in template. See demo project: \"/src/demo/header/HeaderController.js\". More details \u0026 examples: [Extend Component](#extend-component)\n- ``data``:*(optional. deprecated)*. Contains predefined data for component. Better to store data in ``data-***.json`` files.  \n\n#### API: Component object\n``component`` object available in component server side templates. You can extend object with ``controller`` property in ``config.json`` file.\n- ``component.renderComponent(componentName, componentOptions)``: The same as ``page.renderComponent(componentName, componentOptions)``. See: **[\"API: Page Object\"](#api-page-object)** section\n- ``component.initComponent(componentName, componentOptions)``: The same as ``page.initComponent(componentName, componentOptions)``. See: **[\"API: Page Object\"](#api-page-object)** section\n- ``component.render()``: Generate HTML for component and fill with data.\n- ``component._getFile(filePath)``: Get file content. Path should be *relative* to component folder.\n- ``component.config``: JSON from *\"config.json\"*. See description in: **[config.json](#configjson)** section.\n- ``component.data``: JSON from _\"data-***.json\"_ file or *\"data\"* value (if object) from component options. See description in: **[Component options](#component-options)** section.\n- ``component.name``: Component name.\n- ``component.id``: Unique component ID.\n- ``component.options``: Component options (second argument in call **.renderComponent(componentName, componentOptions)**). See description in: **[Component options](#component-options)** section.\n- ``component.mayCustomMethod(arg1, arg2, ...)``: Any method declared in object exported by js defined via *\"config.json \u003e controller\"* property.\n\n#### Extend Component\nComponent's controller helps developer to extend [base component API](#api-component-object) with new functionality.\n\nController is a regular js object(prototype), which exported from js file. Path to js file you can define in [config.json](#configjson) (``controller`` property).\n\n``init()`` function is required, but you can add another methods for you component and then call them in template.\n\n**NOTE:** If you have changed controller content(js file), you need to restart server (because it uses native ``require()`` and ``module.exports`` - cached).\n\n**Example 1:**\n\n*/src/my-math/numbers/config.json*\n```json\n{\n  \"view\": \"NumbersView.html\",\n  \"controller\": \"NumbersController.js\" \n}\n```\n\n*/src/my-math/numbers/NumbersController.js*\n```js\nmodule.exports = {\n    init() {\n        // this === component\n        // Will be invoked, when component has been created\n        console.log(`\"${this.name}\" was created.`);         \n    },\n\n    // Extend base component with new functionality\n    getPositiveNumbers(nums = []) {\n        // this === component\n        return nums.filter((num) =\u003e num \u003e 0);\n    },\n    getNegativeNumbers(nums = []) {\n        // this === component\n        return nums.filter((num) =\u003e num \u003c 0);\n    }\n}\n```\n\n*/src/my-math/numbers/NumbersView.html*\n```html\n\u003cdiv class=\"c-numbers\"\u003e\n    \u003cdiv\u003e\u003cstrong\u003ePositive numbers:\u003c/strong\u003e {{= component.getPositiveNumbers([-1, 10, 100, 0, -200, -345]).join(',') }}\u003c/div\u003e\n    \u003cdiv\u003e\u003cstrong\u003eNegative numbers:\u003c/strong\u003e {{= component.getNegativeNumbers([-1, 10, 100, 0, -200, -345]).join(',') }}\u003c/div\u003e\n\u003c/div\u003e\n```\n\n#### Component options\nAny object can be passed. Reserved properties:\n- ``data``: Dataset for component(pre filled data). Can be **string** or **object**. Accessible via ``component.data`` property or ``data`` in [component template](#component-view-template).\n    - ``string``: Dataset will be read from **${componentName}/data-${dataValue}.json** file. You can use **@extend: \"path/to/data-parent.json\"** notation. It helps to prevent copy-past data between json files. \n    - ``object``: Will be used as it is.\n\n#### Component view (template)\nCurrently our system allow to use only [doT.js](https://olado.github.io/doT/index.html) templates. Small overview you can find in **[Templates](#templates)** section. \n\nNext objects are available in component view:\n- ``page``: page object. See: **[\"API: Page Object\"](#api-page-object)** section.\n- ``component``: component object. See: **[\"API: Component Object\"](#api-component-object)** section.\n- ``data``: Component data. Shortcut for ``component.data``.\n- ``options``: Component init options. Shortcut for ``component.options``.\n- ``partial(data)``: Function to inject HTML from another file.\n \n### Templates\nSmall overview what features can be used in our **[page](#page-template-structure)** \u0026 **[component](#component-view-template)** templates(views). Full documentation you can find [here](https://olado.github.io/doT/index.html).\n- ``{{ js_code }}``: Evaluate javascript code on server side. Example: \n```js\n{{\n    let myFileContent = component._getFile('./my-file.txt');\n    console.log(`Request to: ${page.request.originalUrl}`);\n    const rndm = Math.random() * 1000;\n}}\n```  \n- ``{{= expression }}``: Interpolation. Print expression result. Example: \n```js\nFile content: {{= myFileContent )) \nRandom number: {{= Math.random() }}\nButton component: {{= page.randomComponent('demo/button', { data: { title: 'My button' } }) }}\n```\n- ``{{! expression }}``: Interpolation with encoding. The same as regular interpolation, but safer(prevent script injection). \n- ``{{? condition }}``:\tConditionals. Example:\n```html\n{{? data.image }} \u003cimg src=\"{{= data.image }}\" alt=\"{{= data.alt}}\" /\u003e {{?}}\n{{? Math.random() \u003e 0.5 }}\n    \u003c!-- if: number is bigger than 0.5 --\u003e\n{{??}}\n    \u003c!-- else: (0.5 and lower) --\u003e\n{{?}}    \n```\n- ``{{~ arrayList :item:index }} ...iteration block... {{~}}``: Array iteration(``arrayList.forEach(function (item, index) { ...iteration block... });``). ``item`` and ``index`` can be any variables. \n```html\n\u003cul\u003e\n{{~ ['hello', 'world', 'frontend'] :word:index }}\n    \u003cli\u003e {{! index }} {{! word }} \u003c/li\u003e\n{{~}}\n\u003c/ul\u003e\n```\n\n\n### Command Line Interface\nSee: [@exadel/server-sketch-cli](https://www.npmjs.com/package/@exadel/server-sketch-cli) | [git](https://github.com/exadel-inc/server-sketch-cli)\n\n### FAQ\nWill be updated. If you have any questions(❓) or ideas(💡) or bugs(🐞), you can ask with \"[Issues](./../../issues)\". We are open to help you: 🌞\n\n#### AEM / ui.frontend integration\nOur server is more easier way for frontend developers to work on projects with AEM backend.\n\n**Installing:**\n1) Go to ui.frontend folder: ``cd ui.frontend``\n2) Then regular [Install](#install)\n3) Configure it (maybe, need to add folder, with your css \u0026 js bundles to ``jscssDestFolders`` option)\n\n**Approach:**\n\nMain idea - frontend developers should working in their ecosystem and should not touch AEM on implementing phase.\n1) Frontend developers are working in ``ui.frontend`` folder\n2) New components will be placed under: ``ui.frontend/src/${groupName}/${componentName}`` structure.\n3) ui resources for your component should be placed under: ``ui.frontend/src/${groupName}/${componentName}/ui/`` folder.\n4) Frontend developers are implementing components views with doT.js\n5) After, when component will be ready, AEM developer can migrate it to HTL template engine (we are going to add supporting [HTL](https://experienceleague.adobe.com/docs/experience-manager-htl/using/getting-started/getting-started.html?lang=en#getting-started) on sketch-server in the future)\n\n**NOTE**: Server Sketch doesn't provide any API to build css \u0026 js resources for your project. It is up to you.\n\n\n### Roadmap\nTBD\n\n### Features\n- [x] [file-folder routing](#routing-based-on-file-system)\n- [x] Flexible templates\n- [x] Pages\n- [x] Layouts\n- [x] Components\n- [x] JSON data for components\n- [x] Controllers\n- [x] OOTB BrowserSync integration \n- [x] etc. \n\n### Links \n- [@exadel/server-sketch](https://www.npmjs.com/package/@exadel/server-sketch) | [git](https://github.com/exadel-inc/server-sketch)\n- [@exadel/server-sketch-cli](https://www.npmjs.com/package/@exadel/server-sketch-cli) | [git](https://github.com/exadel-inc/server-sketch-cli)\n- [Exadel Inc. Github](https://github.com/exadel-inc/) | [web-site](https://exadel.com)\n\n🧰 \n\n---\n**Exadel, Inc.**\n\n[![](docs/images/exadel-logo.png)](https://exadel.com)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexadel-inc%2Fserver-sketch","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fexadel-inc%2Fserver-sketch","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fexadel-inc%2Fserver-sketch/lists"}