├── .gitignore ├── .vscode └── launch.json ├── README.md ├── dist ├── attributes.js ├── attributes.js.map ├── class-name.js ├── class-name.js.map ├── custom-types │ ├── colors.js │ ├── colors.js.map │ ├── css-properties.js │ ├── css-properties.js.map │ ├── css-property-values.js │ ├── css-property-values.js.map │ ├── tag-names.js │ ├── tag-names.js.map │ ├── types.js │ └── types.js.map ├── generation │ ├── css-generator.js │ ├── css-generator.js.map │ ├── html-generator.js │ └── html-generator.js.map ├── hobo.js ├── hobo.js.map ├── hobo.min.mjs ├── hobo.min.mjs.gz ├── hobo.mjs ├── hobo.umd.js ├── hobo.umd.min.js ├── hobo.umd.min.js.gz ├── style.js ├── style.js.map ├── tag-builder.js ├── tag-builder.js.map ├── tag.js ├── tag.js.map ├── types │ ├── attributes.d.ts │ ├── class-name.d.ts │ ├── custom-types │ │ ├── colors.d.ts │ │ ├── css-properties.d.ts │ │ ├── css-property-values.d.ts │ │ ├── tag-names.d.ts │ │ └── types.d.ts │ ├── generation │ │ ├── css-generator.d.ts │ │ └── html-generator.d.ts │ ├── hobo.d.ts │ ├── style.d.ts │ ├── tag-builder.d.ts │ ├── tag.d.ts │ └── util.d.ts ├── util.js └── util.js.map ├── docs ├── .nojekyll ├── assets │ ├── highlight.css │ ├── main.js │ ├── navigation.js │ ├── search.js │ └── style.css ├── classes │ ├── attributes.AttrSet.html │ ├── class_name.ClassName.html │ ├── style.StyleSet.html │ ├── tag.Tag.html │ └── tag_builder.TagBuilder.html ├── functions │ ├── hobo.attach.html │ ├── hobo.detach.html │ ├── hobo.doc.html │ ├── hobo.generate.html │ ├── util.camelToDash.html │ ├── util.dashToCamel.html │ ├── util.generateId.html │ ├── util.isObject.html │ ├── util.justFnBody.html │ └── util.replaceDoubleQuotes.html ├── index.html ├── modules │ ├── attributes.html │ ├── class_name.html │ ├── hobo.html │ ├── style.html │ ├── tag.html │ ├── tag_builder.html │ └── util.html ├── types │ └── tag_builder.PickArgType.html └── variables │ ├── hobo._context.html │ ├── hobo.builders.html │ └── tag_builder.builders.html ├── examples ├── _test.ts ├── generated │ ├── readme-1.html │ ├── tag-attributes.html │ ├── tag-classes.html │ ├── tag-inline-styles.html │ ├── tag-scripts.html │ └── tag-styles.html ├── hobo-in-js.js ├── readme-1.ts ├── tag-attributes.ts ├── tag-classes.ts ├── tag-inline-styles.ts ├── tag-script.ts └── tag-style.ts ├── hobo-header.png ├── jest.config.js ├── package-lock.json ├── package.json ├── scripts └── generate_api_docs.js ├── src ├── attributes.ts ├── class-name.ts ├── custom-types │ ├── colors.ts │ ├── css-properties.ts │ ├── css-property-values.ts │ ├── tag-names.ts │ └── types.ts ├── generation │ ├── css-generator.ts │ └── html-generator.ts ├── hobo.ts ├── style.ts ├── tag-builder.ts ├── tag.ts └── util.ts ├── tests └── unit │ ├── css-generator.test.ts │ ├── hobo.test.ts │ ├── html-generator.test.ts │ ├── miscelaneous.test.ts │ ├── tag-builder.test.ts │ └── tag.test.ts ├── tsconfig.build.json └── tsconfig.json /.gitignore: -------------------------------------------------------------------------------- 1 | /_* 2 | /_*/ 3 | /node_modules/ 4 | /src/*.html 5 | /src/*.js 6 | /src/*.js.map 7 | /coverage/ -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | // Use IntelliSense to learn about possible attributes. 3 | // Hover to view descriptions of existing attributes. 4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 5 | "version": "0.2.0", 6 | "configurations": [ 7 | 8 | { 9 | "type": "node", 10 | "request": "launch", 11 | "name": "Debug Program", 12 | "program": "${workspaceFolder}/example/scripts-and-js.ts", 13 | "preLaunchTask": "tsc: build - ts-config.json", 14 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 15 | } 16 | ] 17 | } -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Hobo.js 2 | ![](./hobo-header.png) 3 | 4 | 5 | Welcome to Hobo. A little utility to generate html inside your js/ts code. Meant as a side-project, but after writing it I thought it might be useful to some people in some scenarios. 6 | 7 | 8 | ### Who's this for? 9 | 10 | I have no idea! I might use it some time. But if you use it, and feel like letting me know, you can either leave a star, contribute or reach out! I would be interested in knowing how it's being used, if at all! 11 | 12 | ### What does it do? 13 | 14 | Well, in essence it allows us to create html documents inside js or ts with ease. If I've not missed anything obvious, I think it's possible to generate any kind of html document. Or maybe other stuff like XML too 🤷🏻‍♂️ 15 | 16 | You can generate any tag you want. Add **classes**, **ids**, **styles**, and **attributes**. Create **css** and add **scripts**. All of this, with a quite simple api. I know it might look weird at first, but it's quite intuitive when you gget the hang (_it's quite fast, api is quite small_). 17 | 18 | It even handles self closing tags (void elements) for you. So when creating a **img** tag, it will know it's self closing. 19 | 20 | Additionally it's almost fully typed, not only the surface, but into most css property values! IDEs will help autocomplete a lot of stuff! And is extensively tested. 21 | 22 | > NOTE: If you want a similar API but for creating apps at Runtime, take a look at my other project [Cardboard](https://github.com/nombrekeff/cardboard-js). 23 | 24 | ### Getting Started 25 | Install package: 26 | 27 | ``` 28 | npm install https://github.com/nombrekeff/hobo-js 29 | npm install hobo-js 30 | ``` 31 | 32 | Then you can import the package. 33 | 34 | ```ts 35 | import { builders, generate } from 'hobo-js'; 36 | // Or 37 | const { builders, generate } = require('hobo-js') 38 | ``` 39 | 40 | I recommend destructuring builders, for cleaner code: 41 | 42 | ```ts 43 | const { div, p, span, b, script, button, style, a, hr } = builders; 44 | ``` 45 | 46 | ### Examples 47 | 48 | Check out the [`examples`](/examples) folder for a variety of examples on how to use hobo. 49 | 50 | You can check the interactive demo [here](https://nombrekeff.github.io/hobo-interactive-demo/) 51 | 52 | ### Docs 53 | You can find docs [here](https://nombrekeff.github.io/hobo-js/) 54 | 55 | ### Demo 56 | 57 | Let me show you a little sample (_I explain everything in detail below_) 58 | 59 | ```ts 60 | const myPage = doc('My Page Title'); 61 | 62 | myPage.head.append( 63 | style({ 64 | '.wrapper': { 65 | background: 'black', 66 | display: 'flex', 67 | alignItems: 'center', 68 | justifyContent: 'center', 69 | ':hover': { 70 | background: 'red' 71 | } 72 | }, 73 | }, {/* more style objects */}), 74 | ); 75 | 76 | div.attach.ac('wrapper').build( 77 | p("I'm a child of div.wrapper"), 78 | b.addStyle('color', 'aliceblue')('And so am I'), 79 | hr, 80 | a.addAttr('href', 'http://example.com').b('Click me'), 81 | button.id('button-id').b("I'm also a child"), 82 | ); 83 | 84 | script.a(() => { 85 | const btn = document.querySelector('#button-id'); 86 | }, () => {/* more js */}); 87 | 88 | console.log(generate(myPage.doc)); 89 | ``` 90 | 91 | The above snippet would output the following html: 92 | ```html 93 | 94 | 95 | My Page Title 96 | 107 | 108 | 109 |
110 |

I'm a child of div.wrapper

111 | And so am I 112 |
113 | Click me 114 |
115 | 118 | 119 | 120 | ``` 121 | 122 | #### Demo explanation 123 | 124 | ```ts 125 | const myPage = doc('My Page Title'); 126 | ``` 127 | 128 | First of all we create an HTML Page, by calling `doc()`. This will create an HTML, head and body tags. And returns 3 tags, `doc`, `head` and `body`. You can pass in an optional `title`, and `attachMode` arguments (_look at the [Attaching](#attaching) section for more info_). 129 | 130 | > It's not required to create a doc, you can start the document with any tag you want. 131 | > It also "attaches" the `body` tag to the hobo context. This means that you can then automatically add tags to the attached tag without having to use `.append`. I will explain further down. 132 | > * `doc(mode)`, the doc can receive an argument to change the Attach behaviour. You can specify if you want to attach to the body, head or HTML tags. It will attach to the body by default 133 | 134 | ---- 135 | 136 | ```ts 137 | myPage.head.append( 138 | style({ 139 | '.wrapper': { 140 | background: 'black', 141 | display: 'flex', 142 | alignItems: 'center', 143 | justifyContent: 'center', 144 | ':hover': { 145 | background: 'red' 146 | } 147 | }, 148 | }, { 149 | 'div': {...} 150 | }), 151 | ); 152 | ``` 153 | 154 | The above code adds a style tag to the `head` tag. The style tag accepts objects with css definitions. It's typed, so intelisense will work. 155 | You can pass any number of style objects. And you can also nest like in SCSS. 156 | 157 | For example, this: 158 | ```ts 159 | '.wrapper': { 160 | background: 'blue' 161 | ':hover': { 162 | background: 'red' 163 | } 164 | } 165 | ``` 166 | 167 | Will generate: 168 | ```css 169 | .wrapper { background: 'blue' } 170 | .wrapper:hover { background: 'red' } 171 | ``` 172 | 173 | > Note that in this case we use `.append` as the head is not attached. 174 | 175 | ---- 176 | 177 | ```ts 178 | div.a.ac('wrapper').build( 179 | p("I'm a child of div.wrapper"), 180 | b.addStyle('color', 'aliceblue')('And so am I'), 181 | hr, 182 | a.addAttr('href', 'http://example.com').b('Click me'), 183 | button.id('button-id').b("I'm also a child"), 184 | ); 185 | ``` 186 | 187 | In the step above step, we create the HTML that will be inside the `body`. As you can see instead of calling `.append` in the body tag like with the style. 188 | `.a` and `.attach` are used to [attach](#attaching) to the current hobo context's attached tag (_which will be the body tag in the example_) 189 | 190 | Then we set the tag's class name by calling `.ac` (you can also use `.addClass()`), this will set the class wrapper to the div (`
`) 191 | 192 | After that, we build the tag by calling `.b` (or `.build()`, or you can also call the builder `div()`), and pass in a list of children. 193 | 194 | Hobo uses the builder pattern to ease the creation of tags. A tag can be built by, either calling the builder directly: 195 | ```ts 196 | p("I'm a child of div.wrapper"); 197 | ``` 198 | or by calling the `.b` method: 199 | ```ts 200 | b.build('And so am I'), 201 | ``` 202 | 203 | > NOTE that it's not required to build the tag if you don't need to pass children to it: 204 | > ```ts 205 | > div( 206 | > hr, 207 | > div.addClass('white-box'), 208 | > ) 209 | > ``` 210 | 211 | > **NOTE** Each time you modify the builder it returns a new TagBuilder instance. 212 | > So you can't assign it and then modify it, you need to chain. 213 | > This is done so Hobo can generate new tag builders for each tag without needing to be a function. 214 | > 215 | > This does not work: 216 | > ```ts 217 | > const tag = div.ac('cl'); // .ac is shorthand for addClass 218 | > tag.append(p()); // Will not affect `tag` 219 | > ``` 220 | 221 | > Instead use chaining: 222 | > ```ts 223 | > const tag = div.ac('cl').append(p()); 224 | > ``` 225 | 226 | You can set any attribute by using `.aa`, or `.am`: 227 | ```ts 228 | a.addAttr('href', 'http://example.com'); 229 | a.aa('href', 'http://example.com'); 230 | a.setAttr({ 'href': 'http://example.com' }); 231 | a.sa({ 'href': 'http://example.com' }); 232 | ``` 233 | 234 | ---- 235 | 236 | ```ts 237 | p.addStyle('color', 'aliceblue'); 238 | p.as('color', 'aliceblue'); 239 | p.setStyle({ 'color': 'aliceblue' }); 240 | p.ss({ 'color': 'aliceblue' }); 241 | ``` 242 | Tags can also have inline styles. You can add a single style using the add style (`.as`) method, or add multiple at once using the set styles (`.ss`) method. 243 | 244 | ---- 245 | 246 | ```ts 247 | script.a(() => { 248 | const btn = document.querySelector('#button-id'); 249 | }, () => { 250 | // More JS 251 | }); 252 | ``` 253 | 254 | Creates a `script` tag. Anything inside the function will be inserted into the generated script. 255 | The script accepts a list of functions. You will also have complete typing for dom. 256 | 257 | ---- 258 | 259 | ```ts 260 | console.log(generate(myPage.doc)); 261 | ``` 262 | 263 | Finally, generate the html. `generate` returns a string. It's up to you to handle it from here. 264 | 265 | > NOTE that generate must receive the root tag you want to generate. 266 | > In this example we pass in `myPage.doc` to generate the whole page. 267 | > 268 | > But you can generate any tag you want: 269 | > ```ts 270 | > generate(div(p('Hello'), p('world'))); 271 | > ``` 272 | 273 | 274 | ### Attaching 275 | 276 | by calling `attach` on a tag, it makes it so that you don't need to manually add children to a tag: 277 | 278 | ```ts 279 | const root = div.build("I'm the root tag"); 280 | 281 | attach(root); 282 | div.a; 283 | div.attach; 284 | p.attach; 285 | ``` 286 | 287 | > Any tag that calls `.a` or `.attach` will be added as children of the root div. 288 | 289 | You can also attach multiple times: 290 | ```ts 291 | const root = div.build("I'm the root tag"); 292 | 293 | attach(root); 294 | div.a; 295 | const child = div.attach.addClass('child-wrapper'); 296 | 297 | attach(child); 298 | p.a("I'm a child of child-wrapper"); 299 | p.a("And so am I"); 300 | detach(); 301 | 302 | p.a("I'm now a child of root div again"); 303 | detach(); 304 | 305 | p.a("I'm now not attached as there are no attached tags"); 306 | ``` 307 | 308 | By calling attach the last tag is attached to, then by calling detach, the previously attached tag is now attached. Note that if you call attach and there's only 1 tag attached, it will detach that also. So consequent tags that try to attach will not attach as there's no tag attached. 309 | 310 | When calling `doc()`, it will automatically attach to the body. Although you can specify the tag you want to attach to by passing the attachMode argument. 311 | -------------------------------------------------------------------------------- /dist/attributes.js: -------------------------------------------------------------------------------- 1 | import { ClassName } from './class-name'; 2 | import { StyleSet } from './style'; 3 | /** 4 | * Represents a tag's attribute set. 5 | */ 6 | export class AttrSet { 7 | constructor() { 8 | this.className = new ClassName(); 9 | this.style = new StyleSet(); 10 | this.additionalAttributes = {}; 11 | } 12 | copy() { 13 | const newSet = new AttrSet(); 14 | newSet.className = this.className.copy(); 15 | newSet.id = this.id; 16 | newSet.style = this.style.copy(); 17 | newSet.additionalAttributes = Object.assign({}, this.additionalAttributes); 18 | return newSet; 19 | } 20 | /** Set single attribute */ 21 | set(key, value) { 22 | this.additionalAttributes[key] = value; 23 | return this; 24 | } 25 | /** Remove attributes */ 26 | remove(...attrs) { 27 | for (const cn of attrs) { 28 | delete this.additionalAttributes[cn]; 29 | } 30 | return this; 31 | } 32 | /** Check if an attribute is set */ 33 | has(key) { 34 | return key in this.additionalAttributes; 35 | } 36 | } 37 | //# sourceMappingURL=attributes.js.map -------------------------------------------------------------------------------- /dist/attributes.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"attributes.js","sourceRoot":"","sources":["../src/attributes.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,cAAc,CAAC;AACzC,OAAO,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEnC;;GAEG;AACH,MAAM,OAAO,OAAO;IAApB;QACE,cAAS,GAAc,IAAI,SAAS,EAAE,CAAC;QAEvC,UAAK,GAAa,IAAI,QAAQ,EAAE,CAAC;QAEjC,yBAAoB,GAA8B,EAAE,CAAC;IA6BvD,CAAC;IA3BC,IAAI;QACF,MAAM,MAAM,GAAG,IAAI,OAAO,EAAE,CAAC;QAC7B,MAAM,CAAC,SAAS,GAAG,IAAI,CAAC,SAAS,CAAC,IAAI,EAAE,CAAC;QACzC,MAAM,CAAC,EAAE,GAAG,IAAI,CAAC,EAAE,CAAC;QACpB,MAAM,CAAC,KAAK,GAAG,IAAI,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC;QACjC,MAAM,CAAC,oBAAoB,qBAAQ,IAAI,CAAC,oBAAoB,CAAE,CAAC;QAC/D,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,2BAA2B;IAC3B,GAAG,CAAC,GAAW,EAAE,KAAa;QAC5B,IAAI,CAAC,oBAAoB,CAAC,GAAG,CAAC,GAAG,KAAK,CAAC;QACvC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wBAAwB;IACxB,MAAM,CAAC,GAAG,KAAe;QACvB,KAAK,MAAM,EAAE,IAAI,KAAK,EAAE;YACtB,OAAO,IAAI,CAAC,oBAAoB,CAAC,EAAE,CAAC,CAAC;SACtC;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,mCAAmC;IACnC,GAAG,CAAC,GAAW;QACb,OAAO,GAAG,IAAI,IAAI,CAAC,oBAAoB,CAAC;IAC1C,CAAC;CACF"} -------------------------------------------------------------------------------- /dist/class-name.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a tag's class list. 3 | */ 4 | export class ClassName { 5 | constructor(classNames = []) { 6 | this.classNames = []; 7 | this.classNames = classNames; 8 | } 9 | copy() { 10 | const newClassName = new ClassName(); 11 | newClassName.classNames = [...this.classNames]; 12 | return newClassName; 13 | } 14 | /** 15 | * Returns a string of all the class names separated by spaces. 16 | */ 17 | raw() { 18 | return this.classNames.join(' '); 19 | } 20 | /** Add one or more class names */ 21 | add(...classNames) { 22 | for (const cn of classNames) { 23 | if (!this.has(cn)) { 24 | this.classNames.push(cn); 25 | } 26 | } 27 | return this; 28 | } 29 | /** Remove one or more class names */ 30 | remove(...classNames) { 31 | for (const cn of classNames) { 32 | if (!this.has(cn)) 33 | return this; 34 | const index = this.classNames.indexOf(cn); 35 | this.classNames.splice(index, 1); 36 | } 37 | return this; 38 | } 39 | /** Check if a class name is present. */ 40 | has(str) { 41 | return this.classNames.includes(str); 42 | } 43 | } 44 | //# sourceMappingURL=class-name.js.map -------------------------------------------------------------------------------- /dist/class-name.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"class-name.js","sourceRoot":"","sources":["../src/class-name.ts"],"names":[],"mappings":"AAAA;;GAEG;AACH,MAAM,OAAO,SAAS;IAGpB,YAAY,aAAuB,EAAE;QAFrC,eAAU,GAAa,EAAE,CAAC;QAGxB,IAAI,CAAC,UAAU,GAAG,UAAU,CAAC;IAC/B,CAAC;IAED,IAAI;QACF,MAAM,YAAY,GAAG,IAAI,SAAS,EAAE,CAAC;QACrC,YAAY,CAAC,UAAU,GAAG,CAAC,GAAG,IAAI,CAAC,UAAU,CAAC,CAAC;QAC/C,OAAO,YAAY,CAAC;IACtB,CAAC;IAED;;OAEG;IACH,GAAG;QACD,OAAO,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;IACnC,CAAC;IAED,kCAAkC;IAClC,GAAG,CAAC,GAAG,UAAoB;QACzB,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE;YAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,EAAE;gBACjB,IAAI,CAAC,UAAU,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;aAC1B;SACF;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,qCAAqC;IACrC,MAAM,CAAC,GAAG,UAAoB;QAC5B,KAAK,MAAM,EAAE,IAAI,UAAU,EAAE;YAC3B,IAAI,CAAC,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC;gBAAE,OAAO,IAAI,CAAC;YAE/B,MAAM,KAAK,GAAG,IAAI,CAAC,UAAU,CAAC,OAAO,CAAC,EAAE,CAAC,CAAC;YAC1C,IAAI,CAAC,UAAU,CAAC,MAAM,CAAC,KAAK,EAAE,CAAC,CAAC,CAAC;SAClC;QAED,OAAO,IAAI,CAAC;IACd,CAAC;IAED,wCAAwC;IACxC,GAAG,CAAC,GAAW;QACb,OAAO,IAAI,CAAC,UAAU,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;IACvC,CAAC;CACF"} -------------------------------------------------------------------------------- /dist/custom-types/colors.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=colors.js.map -------------------------------------------------------------------------------- /dist/custom-types/colors.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"colors.js","sourceRoot":"","sources":["../../src/custom-types/colors.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/custom-types/css-properties.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=css-properties.js.map -------------------------------------------------------------------------------- /dist/custom-types/css-properties.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"css-properties.js","sourceRoot":"","sources":["../../src/custom-types/css-properties.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/custom-types/css-property-values.js: -------------------------------------------------------------------------------- 1 | export {}; 2 | //# sourceMappingURL=css-property-values.js.map -------------------------------------------------------------------------------- /dist/custom-types/css-property-values.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"css-property-values.js","sourceRoot":"","sources":["../../src/custom-types/css-property-values.ts"],"names":[],"mappings":""} -------------------------------------------------------------------------------- /dist/custom-types/tag-names.js: -------------------------------------------------------------------------------- 1 | /** 2 | * @export 3 | * List of all known self-closing HTML tags 4 | */ 5 | export const selfClosingTags = [ 6 | 'area', 7 | 'base', 8 | 'br', 9 | 'col', 10 | 'embed', 11 | 'hr', 12 | 'img', 13 | 'input', 14 | 'link', 15 | 'meta', 16 | 'param', 17 | 'source', 18 | 'track', 19 | 'wbr', 20 | ]; 21 | /** List of all known closing HTML tags */ 22 | export const closingTags = [ 23 | 'a', 24 | 'abbr', 25 | 'acronym', 26 | 'address', 27 | 'article', 28 | 'aside', 29 | 'audio', 30 | 'b', 31 | 'basefont', 32 | 'bdi', 33 | 'bdo', 34 | 'big', 35 | 'blockquote', 36 | 'body', 37 | 'button', 38 | 'canvas', 39 | 'caption', 40 | 'center', 41 | 'cite', 42 | 'code', 43 | 'colgroup', 44 | 'data', 45 | 'datalist', 46 | 'dd', 47 | 'del', 48 | 'details', 49 | 'dfn', 50 | 'dialog', 51 | 'div', 52 | 'dl', 53 | 'dt', 54 | 'em', 55 | 'fieldset', 56 | 'figcaption', 57 | 'figure', 58 | 'footer', 59 | 'form', 60 | 'h1', 61 | 'h2', 62 | 'h3', 63 | 'h4', 64 | 'h5', 65 | 'h6', 66 | 'head', 67 | 'header', 68 | 'html', 69 | 'i', 70 | 'iframe', 71 | 'ins', 72 | 'kbd', 73 | 'label', 74 | 'legend', 75 | 'li', 76 | 'main', 77 | 'map', 78 | 'mark', 79 | 'meter', 80 | 'nav', 81 | 'noscript', 82 | 'object', 83 | 'ol', 84 | 'optgroup', 85 | 'option', 86 | 'output', 87 | 'p', 88 | 'picture', 89 | 'pre', 90 | 'progress', 91 | 'q', 92 | 'rp', 93 | 'rt', 94 | 'ruby', 95 | 's', 96 | 'samp', 97 | 'script', 98 | 'section', 99 | 'select', 100 | 'small', 101 | 'span', 102 | 'strong', 103 | 'style', 104 | 'sub', 105 | 'summary', 106 | 'sup', 107 | 'svg', 108 | 'table', 109 | 'tbody', 110 | 'td', 111 | 'template', 112 | 'textarea', 113 | 'tfoot', 114 | 'th', 115 | 'thead', 116 | 'time', 117 | 'title', 118 | 'tr', 119 | 'u', 120 | 'ul', 121 | 'var', 122 | 'video', 123 | ]; 124 | /** List of all known HTML tags */ 125 | export const allKnownTags = [ 126 | 'area', 127 | 'base', 128 | 'br', 129 | 'col', 130 | 'embed', 131 | 'hr', 132 | 'img', 133 | 'input', 134 | 'link', 135 | 'meta', 136 | 'param', 137 | 'source', 138 | 'track', 139 | 'wbr', 140 | 'a', 141 | 'abbr', 142 | 'acronym', 143 | 'address', 144 | 'article', 145 | 'aside', 146 | 'audio', 147 | 'b', 148 | 'basefont', 149 | 'bdi', 150 | 'bdo', 151 | 'big', 152 | 'blockquote', 153 | 'body', 154 | 'button', 155 | 'canvas', 156 | 'caption', 157 | 'center', 158 | 'cite', 159 | 'code', 160 | 'colgroup', 161 | 'data', 162 | 'datalist', 163 | 'dd', 164 | 'del', 165 | 'details', 166 | 'dfn', 167 | 'dialog', 168 | 'div', 169 | 'dl', 170 | 'dt', 171 | 'em', 172 | 'fieldset', 173 | 'figcaption', 174 | 'figure', 175 | 'footer', 176 | 'form', 177 | 'h1', 178 | 'h2', 179 | 'h3', 180 | 'h4', 181 | 'h5', 182 | 'h6', 183 | 'head', 184 | 'header', 185 | 'html', 186 | 'i', 187 | 'iframe', 188 | 'ins', 189 | 'kbd', 190 | 'label', 191 | 'legend', 192 | 'li', 193 | 'main', 194 | 'map', 195 | 'mark', 196 | 'meter', 197 | 'nav', 198 | 'noscript', 199 | 'object', 200 | 'ol', 201 | 'optgroup', 202 | 'option', 203 | 'output', 204 | 'p', 205 | 'picture', 206 | 'pre', 207 | 'progress', 208 | 'q', 209 | 'rp', 210 | 'rt', 211 | 'ruby', 212 | 's', 213 | 'samp', 214 | 'script', 215 | 'section', 216 | 'select', 217 | 'small', 218 | 'span', 219 | 'strong', 220 | 'style', 221 | 'sub', 222 | 'summary', 223 | 'sup', 224 | 'svg', 225 | 'table', 226 | 'tbody', 227 | 'td', 228 | 'template', 229 | 'textarea', 230 | 'tfoot', 231 | 'th', 232 | 'thead', 233 | 'time', 234 | 'title', 235 | 'tr', 236 | 'u', 237 | 'ul', 238 | 'var', 239 | 'video', 240 | ]; 241 | export const storableTags = ['style', 'script']; 242 | //# sourceMappingURL=tag-names.js.map -------------------------------------------------------------------------------- /dist/custom-types/tag-names.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag-names.js","sourceRoot":"","sources":["../../src/custom-types/tag-names.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/custom-types/types.js: -------------------------------------------------------------------------------- 1 | export var AttachMode; 2 | (function (AttachMode) { 3 | AttachMode[AttachMode["none"] = 0] = "none"; 4 | AttachMode[AttachMode["body"] = 1] = "body"; 5 | AttachMode[AttachMode["html"] = 2] = "html"; 6 | AttachMode[AttachMode["head"] = 3] = "head"; 7 | })(AttachMode || (AttachMode = {})); 8 | //# sourceMappingURL=types.js.map -------------------------------------------------------------------------------- /dist/custom-types/types.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/custom-types/types.ts"],"names":[],"mappings":"AAWA,MAAM,CAAN,IAAY,UAKX;AALD,WAAY,UAAU;IACpB,2CAAI,CAAA;IACJ,2CAAI,CAAA;IACJ,2CAAI,CAAA;IACJ,2CAAI,CAAA;AACN,CAAC,EALW,UAAU,KAAV,UAAU,QAKrB"} -------------------------------------------------------------------------------- /dist/generation/css-generator.js: -------------------------------------------------------------------------------- 1 | import { camelToDash, isObject } from '../util'; 2 | export class CssGenerator { 3 | generateCss(styleSheet) { 4 | let stylesheets = styleSheet instanceof Array ? styleSheet : [styleSheet]; 5 | let generatedCss = ''; 6 | for (const sheet of stylesheets) { 7 | for (const key in sheet) { 8 | generatedCss += this.generateBlock(key, sheet[key]); 9 | } 10 | } 11 | return generatedCss; 12 | } 13 | generateBlock(selector, style) { 14 | let blocks = this.generateBlockContent(selector, style); 15 | return blocks.join(''); 16 | } 17 | generateBlockContent(selector, style) { 18 | let inside = ''; 19 | let blocks = []; 20 | for (const key in style) { 21 | if (isObject(style[key])) { 22 | blocks.push(this.generateBlockContent(selector + key, style[key])); 23 | } 24 | else if (style[key]) { 25 | inside += this.generateStyle(key, style[key]); 26 | } 27 | } 28 | blocks.unshift(`${selector}{${inside}}`); 29 | return blocks; 30 | } 31 | generateInline(style) { 32 | let inside = ''; 33 | for (const key in style) { 34 | if (style[key]) { 35 | inside += this.generateStyle(key, style[key]); 36 | } 37 | } 38 | return inside; 39 | } 40 | generateStyle(name, value) { 41 | return `${camelToDash(name)}:${value};`; 42 | } 43 | } 44 | //# sourceMappingURL=css-generator.js.map -------------------------------------------------------------------------------- /dist/generation/css-generator.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"css-generator.js","sourceRoot":"","sources":["../../src/generation/css-generator.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,QAAQ,EAAE,MAAM,SAAS,CAAC;AAEhD,MAAM,OAAO,YAAY;IACvB,WAAW,CAAC,UAAmF;QAC7F,IAAI,WAAW,GAAG,UAAU,YAAY,KAAK,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC;QAC1E,IAAI,YAAY,GAAG,EAAE,CAAC;QAEtB,KAAK,MAAM,KAAK,IAAI,WAAW,EAAE;YAC/B,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE;gBACvB,YAAY,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC;aACrD;SACF;QACD,OAAO,YAAY,CAAC;IACtB,CAAC;IAED,aAAa,CAAC,QAAgB,EAAE,KAAqB;QACnD,IAAI,MAAM,GAAG,IAAI,CAAC,oBAAoB,CAAC,QAAQ,EAAE,KAAK,CAAC,CAAC;QACxD,OAAO,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,CAAC;IACzB,CAAC;IAED,oBAAoB,CAAC,QAAgB,EAAE,KAAqB;QAC1D,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,IAAI,MAAM,GAAG,EAAE,CAAC;QAEhB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE;YACvB,IAAI,QAAQ,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,EAAE;gBACxB,MAAM,CAAC,IAAI,CAAC,IAAI,CAAC,oBAAoB,CAAC,QAAQ,GAAG,GAAG,EAAE,KAAK,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC;aACpE;iBACI,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE;gBACnB,MAAM,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAW,CAAC,CAAC;aACzD;SACF;QAED,MAAM,CAAC,OAAO,CAAC,GAAG,QAAQ,IAAI,MAAM,GAAG,CAAC,CAAC;QAEzC,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,cAAc,CAAC,KAAe;QAC5B,IAAI,MAAM,GAAG,EAAE,CAAC;QAChB,KAAK,MAAM,GAAG,IAAI,KAAK,EAAE;YACvB,IAAI,KAAK,CAAC,GAAG,CAAC,EAAE;gBACd,MAAM,IAAI,IAAI,CAAC,aAAa,CAAC,GAAG,EAAE,KAAK,CAAC,GAAG,CAAW,CAAC,CAAC;aACzD;SACF;QACD,OAAO,MAAM,CAAC;IAChB,CAAC;IAED,aAAa,CAAC,IAAY,EAAE,KAAa;QACvC,OAAO,GAAG,WAAW,CAAC,IAAI,CAAC,IAAI,KAAK,GAAG,CAAC;IAC1C,CAAC;CACF"} -------------------------------------------------------------------------------- /dist/generation/html-generator.js: -------------------------------------------------------------------------------- 1 | import { Tag } from '../tag'; 2 | import { CssGenerator } from './css-generator'; 3 | import { justFnBody } from '../util'; 4 | import { TagBuilder } from '../tag-builder'; 5 | export class HtmlGenerator { 6 | constructor() { 7 | this.cssGenerator = new CssGenerator(); 8 | this.beautifyCss = true; 9 | } 10 | /** Generate html from the tag provided */ 11 | generateHtml(rootTag) { 12 | let generatedHtml = this._generateTag(rootTag); 13 | return generatedHtml; 14 | } 15 | _generateTag(tag) { 16 | if (tag.tagName == 'style') { 17 | return this._createTag(tag, this.cssGenerator.generateCss(tag._meta.storage)); 18 | } 19 | if (tag.tagName == 'script') { 20 | return this._createTag(tag, this._generateScriptContent(tag._meta.storage)); 21 | } 22 | let inside = ''; 23 | for (const child of tag.children) { 24 | let effectiveChild = child; 25 | if (child instanceof TagBuilder) { 26 | effectiveChild = child.b(); 27 | } 28 | if (effectiveChild instanceof Tag) { 29 | inside += this._generateTag(effectiveChild); 30 | } 31 | else { 32 | inside += child; 33 | } 34 | } 35 | return this._createTag(tag, inside); 36 | } 37 | _createTag(tag, inside) { 38 | const attributesString = this._generateAttributeString(tag); 39 | let openTag = [tag.tagName, attributesString].filter((n) => n).join(' '); 40 | if (tag._meta.selfClosing) { 41 | return `<${openTag}/>`; 42 | } 43 | return `<${openTag}>${inside}`; 44 | } 45 | _generateAttributeString(tag) { 46 | let attributesString = ''; 47 | if (tag.attr.id) { 48 | attributesString += this._attr('id', tag.attr.id); 49 | } 50 | attributesString += this._attr('class', tag.attr.className.raw()); 51 | attributesString += this._generateInlineStyle(tag); 52 | for (const key in tag.attr.additionalAttributes) { 53 | attributesString += this._attr(key, tag.attr.additionalAttributes[key]); 54 | } 55 | return attributesString; 56 | } 57 | _generateInlineStyle(tag) { 58 | let styleContent = this.cssGenerator.generateInline(tag.attr.style.styles); 59 | return this._attr('style', styleContent); 60 | } 61 | _generateScriptContent(storage) { 62 | let scriptContent = ''; 63 | if (storage instanceof Function) { 64 | scriptContent += justFnBody(storage); 65 | } 66 | else if (storage instanceof Array) { 67 | for (const fn of storage) { 68 | scriptContent += this._generateScriptContent(fn); 69 | } 70 | } 71 | return scriptContent; 72 | } 73 | _attr(name, value) { 74 | if (!value) 75 | return ''; 76 | return `${name}="${value}"`; 77 | } 78 | } 79 | //# sourceMappingURL=html-generator.js.map -------------------------------------------------------------------------------- /dist/generation/html-generator.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"html-generator.js","sourceRoot":"","sources":["../../src/generation/html-generator.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/hobo.js: -------------------------------------------------------------------------------- 1 | export { Tag } from './tag'; 2 | import { HtmlGenerator } from './generation/html-generator'; 3 | import { AttachMode } from './custom-types/types'; 4 | import { builders as tagBuilders } from './tag-builder'; 5 | export { TagBuilder } from './tag-builder'; 6 | export let _context = { 7 | attachedTag: null, 8 | attachedTagStack: [], 9 | globalStuff: [], 10 | }; 11 | /** 12 | * Creates an HTML document, with a head and body tags. 13 | * You can pass in the AttachMode to attach to different tags. 14 | */ 15 | export function doc(pageTitle = 'New Hobo Document', mode = AttachMode.body) { 16 | const dhead = builders.head 17 | .aa('lang', 'en') 18 | .build(builders.meta.addAttr('charset', 'UTF-8'), builders.meta.setAttr({ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }), builders.title(pageTitle)); 19 | const dbody = builders.body.build(); 20 | const doc = builders.html.build(dhead, dbody); 21 | switch (mode) { 22 | case AttachMode.html: 23 | attach(doc); 24 | break; 25 | case AttachMode.head: 26 | attach(doc.findByTagName('head')); // We know there is a head tag 27 | break; 28 | case AttachMode.body: 29 | attach(doc.findByTagName('body')); // We know there is a body tag 30 | break; 31 | case AttachMode.none: 32 | break; 33 | } 34 | return { doc, head: dhead, body: dbody }; 35 | } 36 | /** 37 | * Attach a given tag to the current context. 38 | * When you attach a tag, this tag will be the "root" for any tag created without a parent. 39 | * 40 | * * If there is not attached tag, it will be attached 41 | * * If there is already a tag attached, it will store the previous tag 42 | * and will set the new tag as the root. After finishing using the tag as the root, you can call `@detach` 43 | * and return to the previous root tag. 44 | * 45 | * This is used to remove clutter and reduntancies when creating hobo docs. 46 | * Like this: 47 | * 48 | * @example 49 | * Simple example with only 1 attach 50 | * ```ts 51 | * const parent = doc(); 52 | * attach(parent); 53 | * 54 | * div(); 55 | * p(); 56 | * ``` 57 | * The `div` and `p` tags will be automatically added as child of `parentDiv` 58 | * 59 | * @example 60 | * Example attaching and detaching 61 | * ```ts 62 | * const parent = doc(); 63 | * attach(parent); 64 | * 65 | * div(); 66 | * p(); 67 | * let d1 = div(); 68 | * attach(d1); 69 | * // All the p tags will be added to `d1` 70 | * p(); 71 | * p(); 72 | * p(); 73 | * // remember to call detach when you want to go back to the previous root tag 74 | * detach(); 75 | * ``` 76 | * 77 | * @param {Tag} tag 78 | */ 79 | export function attach(tag) { 80 | if (_context.attachedTag) { 81 | _context.attachedTagStack.push(_context.attachedTag); 82 | } 83 | _context.attachedTag = tag; 84 | } 85 | /** 86 | * Detached the currently attached tag, and pops back to the previously attached tag. 87 | * If there are no stored tags, it will clear the attached tag. 88 | * You will need to handle the consecuent created tags. 89 | */ 90 | export function detach() { 91 | if (_context.attachedTagStack.length > 0) { 92 | _context.attachedTag = _context.attachedTagStack.pop(); 93 | } 94 | else { 95 | _context.attachedTag = null; 96 | } 97 | } 98 | function makeAttachable(builder) { 99 | const attachFn = () => { 100 | if (_context.attachedTag) { 101 | return builder.p(_context.attachedTag); 102 | } 103 | return builder; 104 | }; 105 | Object.defineProperty(builder, 'a', { 106 | get: attachFn, 107 | }); 108 | Object.defineProperty(builder, 'attach', { 109 | get: attachFn, 110 | }); 111 | return builder; 112 | } 113 | const exportedTagBuilders = {}; 114 | for (let key in tagBuilders) { 115 | exportedTagBuilders[key] = makeAttachable(tagBuilders[key]); 116 | } 117 | const _generator = new HtmlGenerator(); 118 | /** Converts's the Tag tree into a html string */ 119 | export function generate(root) { 120 | return _generator.generateHtml(root); 121 | } 122 | /** 123 | * TagBuilders for each known tag. From `div` to `acronym` 124 | */ 125 | export const builders = exportedTagBuilders; 126 | //# sourceMappingURL=hobo.js.map -------------------------------------------------------------------------------- /dist/hobo.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"hobo.js","sourceRoot":"","sources":["../src/hobo.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/hobo.min.mjs: -------------------------------------------------------------------------------- 1 | class t{constructor(t=[]){this.classNames=[],this.classNames=t}copy(){const e=new t;return e.classNames=[...this.classNames],e}raw(){return this.classNames.join(" ")}add(...t){for(const e of t)this.has(e)||this.classNames.push(e);return this}remove(...t){for(const e of t){if(!this.has(e))return this;const t=this.classNames.indexOf(e);this.classNames.splice(t,1)}return this}has(t){return this.classNames.includes(t)}}class e{constructor(){this.styles={}}copy(){const t=new e;return t.styles=Object.assign({},this.styles),t}set(t,e){return this.styles[t]=e,this}remove(...t){for(const e of t)delete this.styles[e];return this}has(t){return t in this.styles}}class s{constructor(){this.className=new t,this.style=new e,this.additionalAttributes={}}copy(){const t=new s;return t.className=this.className.copy(),t.id=this.id,t.style=this.style.copy(),t.additionalAttributes=Object.assign({},this.additionalAttributes),t}set(t,e){return this.additionalAttributes[t]=e,this}remove(...t){for(const e of t)delete this.additionalAttributes[e];return this}has(t){return t in this.additionalAttributes}}const r=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"],a=["style","script"];class n extends Function{constructor(){super("...args","return this.__self__.__call__(...args)");var t=this.bind(this);return this.__self__=t,t}__call__(...t){}}class i extends n{get className(){return this.attr.className}get tagId(){return this.attr.id}constructor(t,...e){super(),this.children=[],this.attr=new s,this._meta={selfClosing:!1,storesChildren:!1,storage:!1},this.setTagName(t),this.children.push(...e)}copy(){const t=new i(this.tagName,...this.children);return t.attr=this.attr.copy(),t._parent=this._parent,t._meta=Object.assign(Object.assign({},this._meta),t._meta),t}setTagName(t){return this.tagName=this.sanitizeTagName(t),this.validateTagName(this.tagName),this._meta=Object.assign(Object.assign({},this._meta),this.getMetaForTag(this.tagName)),this}b(...t){return this.build(...t)}build(...t){return this.__call__(...t)}__call__(...t){let e=[...this.children,...t];this._meta.storesChildren&&(this._meta.storage=t,e=[]);const s=new d(this.tagName,e,this.attr,this._meta);return this._parent&&this._parent.children.push(s),s}get a(){return this.copy()}get attach(){return this.copy()}p(t){const e=this.copy();return e._parent=t,e}id(t){const e=this.copy();return e.attr.id=t,e}text(t){const e=this.copy();return e.children=[t],e}append(...t){if(this._meta.selfClosing)return this;const e=this.copy();return e.children.push(...t.map((t=>t instanceof i?t.b():t))),e}setChildren(t){const e=this.copy();return e.children=t,e}store(t){const e=this.copy();return e._meta.storage=t,e}m(t){return this.mod(t)}mod(t){const e=this.copy();return t(e),e}mc(t){return this.modClass(t)}modClass(t){return this.copy().m((e=>t(e.className)))}ac(...t){return this.addClass(...t)}addClass(...t){const e=this.copy();return e.className.add(...t),e}rc(...t){return this.rmClass(...t)}rmClass(...t){const e=this.copy();return e.className.remove(...t),e}aa(t,e){return this.addAttr(t,e)}addAttr(t,e){const s=this.copy();return s.attr.set(t,e),s}sa(t){return this.setAttr(t)}setAttr(t){const e=this.copy();return e.attr.additionalAttributes=Object.assign(Object.assign({},e.attr.additionalAttributes),t),e}ra(...t){return this.rmAttr(...t)}rmAttr(...t){const e=this.copy();return e.attr.remove(...t),e}as(t,e){return this.addStyle(t,e)}addStyle(t,e){const s=this.copy();return s.attr.style.set(t,e),s}ss(t){return this.setStyles(t)}setStyles(t){const e=this.copy();return e.attr.style.styles=Object.assign(Object.assign({},e.attr.style.styles),t),e}rs(...t){return this.rmStyle(...t)}rmStyle(...t){const e=this.copy();return e.attr.style.remove(...t),e}getMetaForTag(t){return{selfClosing:this.isSelfClosingTag(t),storesChildren:this.isStorableTag(t),storage:null}}isSelfClosingTag(t){return r.includes(t)}isStorableTag(t){return a.includes(t)}validateTagName(t){if(!/[a-zA-Z_][a-z-A-Z0-9_]*/.test(t))throw new Error(`Invalid tag name "${t}"`)}sanitizeTagName(t){return t.replace(/[^\w\d-_]/,"")}}function o(t,...e){return new i(t,...e)}const c=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr","a","abbr","acronym","address","article","aside","audio","b","basefont","bdi","bdo","big","blockquote","body","button","canvas","caption","center","cite","code","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","html","i","iframe","ins","kbd","label","legend","li","main","map","mark","meter","nav","noscript","object","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","span","strong","style","sub","summary","sup","svg","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","u","ul","var","video"];let l={tag:o};for(let t of c)l[t]=o(t);const h=l;class d{get className(){return this.attr.className}get tagId(){return this.attr.id}constructor(t,e,r,a){this.children=[],this.attr=new s,this._meta={selfClosing:!1,storesChildren:!1,storage:!1},this.tagName=t,this.children=e,this.attr=r,this._meta=a}append(t){t instanceof i&&(t=t.b()),this.children.push(t)}findByTagName(t){return this.findOneBy((e=>e.tagName==t))}findOneBy(t){const e=[];for(e.push(this);e.length>0;){let s=e.pop();if(t(s))return s;if(s.children&&s.children.length)for(let t=0;t`-${t.toLowerCase()}`))}:${e};`;var s}}var g;!function(t){t[t.none=0]="none",t[t.body=1]="body",t[t.html=2]="html",t[t.head=3]="head"}(g||(g={}));let m={attachedTag:null,attachedTagStack:[],globalStuff:[]};function p(t="New Hobo Document",e=g.body){const s=C.head.aa("lang","en").build(C.meta.addAttr("charset","UTF-8"),C.meta.setAttr({name:"viewport",content:"width=device-width, initial-scale=1.0"}),C.title(t)),r=C.body.build(),a=C.html.build(s,r);switch(e){case g.html:f(a);break;case g.head:f(a.findByTagName("head"));break;case g.body:f(a.findByTagName("body"));case g.none:}return{doc:a,head:s,body:r}}function f(t){m.attachedTag&&m.attachedTagStack.push(m.attachedTag),m.attachedTag=t}function y(){m.attachedTagStack.length>0?m.attachedTag=m.attachedTagStack.pop():m.attachedTag=null}function b(t){const e=()=>m.attachedTag?t.p(m.attachedTag):t;return Object.defineProperty(t,"a",{get:e}),Object.defineProperty(t,"attach",{get:e}),t}const _={};for(let t in h)_[t]=b(h[t]);const N=new class{constructor(){this.cssGenerator=new u,this.beautifyCss=!0}generateHtml(t){return this._generateTag(t)}_generateTag(t){if("style"==t.tagName)return this._createTag(t,this.cssGenerator.generateCss(t._meta.storage));if("script"==t.tagName)return this._createTag(t,this._generateScriptContent(t._meta.storage));let e="";for(const s of t.children){let t=s;s instanceof i&&(t=s.b()),e+=t instanceof d?this._generateTag(t):s}return this._createTag(t,e)}_createTag(t,e){const s=this._generateAttributeString(t);let r=[t.tagName,s].filter((t=>t)).join(" ");return t._meta.selfClosing?`<${r}/>`:`<${r}>${e}`}_generateAttributeString(t){let e="";t.attr.id&&(e+=this._attr("id",t.attr.id)),e+=this._attr("class",t.attr.className.raw()),e+=this._generateInlineStyle(t);for(const s in t.attr.additionalAttributes)e+=this._attr(s,t.attr.additionalAttributes[s]);return e}_generateInlineStyle(t){let e=this.cssGenerator.generateInline(t.attr.style.styles);return this._attr("style",e)}_generateScriptContent(t){let e="";if(t instanceof Function)e+=function(t){let e=t.toString();return e=e.replace(/^(.*{)/,""),e=e.replace(/}$/,""),e=e.replace(/^\(.*\)\s?=>\s?{/,""),e.trim()}(t);else if(t instanceof Array)for(const s of t)e+=this._generateScriptContent(s);return e}_attr(t,e){return e?`${t}="${e}"`:""}};function T(t){return N.generateHtml(t)}const C=_;export{d as Tag,i as TagBuilder,m as _context,f as attach,C as builders,y as detach,p as doc,T as generate}; -------------------------------------------------------------------------------- /dist/hobo.min.mjs.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nombrekeff/hobo-js/1cd54a3fe83bbd15820fb2e05fb6736a35efba6b/dist/hobo.min.mjs.gz -------------------------------------------------------------------------------- /dist/hobo.umd.min.js: -------------------------------------------------------------------------------- 1 | !function(t,e){"object"==typeof exports&&"undefined"!=typeof module?e(exports):"function"==typeof define&&define.amd?define(["exports"],e):e((t="undefined"!=typeof globalThis?globalThis:t||self).sayHello={})}(this,(function(t){"use strict";class e{constructor(t=[]){this.classNames=[],this.classNames=t}copy(){const t=new e;return t.classNames=[...this.classNames],t}raw(){return this.classNames.join(" ")}add(...t){for(const e of t)this.has(e)||this.classNames.push(e);return this}remove(...t){for(const e of t){if(!this.has(e))return this;const t=this.classNames.indexOf(e);this.classNames.splice(t,1)}return this}has(t){return this.classNames.includes(t)}}class s{constructor(){this.styles={}}copy(){const t=new s;return t.styles=Object.assign({},this.styles),t}set(t,e){return this.styles[t]=e,this}remove(...t){for(const e of t)delete this.styles[e];return this}has(t){return t in this.styles}}class a{constructor(){this.className=new e,this.style=new s,this.additionalAttributes={}}copy(){const t=new a;return t.className=this.className.copy(),t.id=this.id,t.style=this.style.copy(),t.additionalAttributes=Object.assign({},this.additionalAttributes),t}set(t,e){return this.additionalAttributes[t]=e,this}remove(...t){for(const e of t)delete this.additionalAttributes[e];return this}has(t){return t in this.additionalAttributes}}const r=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr"],n=["style","script"];class i extends Function{constructor(){super("...args","return this.__self__.__call__(...args)");var t=this.bind(this);return this.__self__=t,t}__call__(...t){}}class o extends i{get className(){return this.attr.className}get tagId(){return this.attr.id}constructor(t,...e){super(),this.children=[],this.attr=new a,this._meta={selfClosing:!1,storesChildren:!1,storage:!1},this.setTagName(t),this.children.push(...e)}copy(){const t=new o(this.tagName,...this.children);return t.attr=this.attr.copy(),t._parent=this._parent,t._meta=Object.assign(Object.assign({},this._meta),t._meta),t}setTagName(t){return this.tagName=this.sanitizeTagName(t),this.validateTagName(this.tagName),this._meta=Object.assign(Object.assign({},this._meta),this.getMetaForTag(this.tagName)),this}b(...t){return this.build(...t)}build(...t){return this.__call__(...t)}__call__(...t){let e=[...this.children,...t];this._meta.storesChildren&&(this._meta.storage=t,e=[]);const s=new u(this.tagName,e,this.attr,this._meta);return this._parent&&this._parent.children.push(s),s}get a(){return this.copy()}get attach(){return this.copy()}p(t){const e=this.copy();return e._parent=t,e}id(t){const e=this.copy();return e.attr.id=t,e}text(t){const e=this.copy();return e.children=[t],e}append(...t){if(this._meta.selfClosing)return this;const e=this.copy();return e.children.push(...t.map((t=>t instanceof o?t.b():t))),e}setChildren(t){const e=this.copy();return e.children=t,e}store(t){const e=this.copy();return e._meta.storage=t,e}m(t){return this.mod(t)}mod(t){const e=this.copy();return t(e),e}mc(t){return this.modClass(t)}modClass(t){return this.copy().m((e=>t(e.className)))}ac(...t){return this.addClass(...t)}addClass(...t){const e=this.copy();return e.className.add(...t),e}rc(...t){return this.rmClass(...t)}rmClass(...t){const e=this.copy();return e.className.remove(...t),e}aa(t,e){return this.addAttr(t,e)}addAttr(t,e){const s=this.copy();return s.attr.set(t,e),s}sa(t){return this.setAttr(t)}setAttr(t){const e=this.copy();return e.attr.additionalAttributes=Object.assign(Object.assign({},e.attr.additionalAttributes),t),e}ra(...t){return this.rmAttr(...t)}rmAttr(...t){const e=this.copy();return e.attr.remove(...t),e}as(t,e){return this.addStyle(t,e)}addStyle(t,e){const s=this.copy();return s.attr.style.set(t,e),s}ss(t){return this.setStyles(t)}setStyles(t){const e=this.copy();return e.attr.style.styles=Object.assign(Object.assign({},e.attr.style.styles),t),e}rs(...t){return this.rmStyle(...t)}rmStyle(...t){const e=this.copy();return e.attr.style.remove(...t),e}getMetaForTag(t){return{selfClosing:this.isSelfClosingTag(t),storesChildren:this.isStorableTag(t),storage:null}}isSelfClosingTag(t){return r.includes(t)}isStorableTag(t){return n.includes(t)}validateTagName(t){if(!/[a-zA-Z_][a-z-A-Z0-9_]*/.test(t))throw new Error(`Invalid tag name "${t}"`)}sanitizeTagName(t){return t.replace(/[^\w\d-_]/,"")}}function c(t,...e){return new o(t,...e)}const l=["area","base","br","col","embed","hr","img","input","link","meta","param","source","track","wbr","a","abbr","acronym","address","article","aside","audio","b","basefont","bdi","bdo","big","blockquote","body","button","canvas","caption","center","cite","code","colgroup","data","datalist","dd","del","details","dfn","dialog","div","dl","dt","em","fieldset","figcaption","figure","footer","form","h1","h2","h3","h4","h5","h6","head","header","html","i","iframe","ins","kbd","label","legend","li","main","map","mark","meter","nav","noscript","object","ol","optgroup","option","output","p","picture","pre","progress","q","rp","rt","ruby","s","samp","script","section","select","small","span","strong","style","sub","summary","sup","svg","table","tbody","td","template","textarea","tfoot","th","thead","time","title","tr","u","ul","var","video"];let h={tag:c};for(let t of l)h[t]=c(t);const d=h;class u{get className(){return this.attr.className}get tagId(){return this.attr.id}constructor(t,e,s,r){this.children=[],this.attr=new a,this._meta={selfClosing:!1,storesChildren:!1,storage:!1},this.tagName=t,this.children=e,this.attr=s,this._meta=r}append(t){t instanceof o&&(t=t.b()),this.children.push(t)}findByTagName(t){return this.findOneBy((e=>e.tagName==t))}findOneBy(t){const e=[];for(e.push(this);e.length>0;){let s=e.pop();if(t(s))return s;if(s.children&&s.children.length)for(let t=0;t`-${t.toLowerCase()}`))}:${e};`;var s}}var m;!function(t){t[t.none=0]="none",t[t.body=1]="body",t[t.html=2]="html",t[t.head=3]="head"}(m||(m={}));let p={attachedTag:null,attachedTagStack:[],globalStuff:[]};function f(t){p.attachedTag&&p.attachedTagStack.push(p.attachedTag),p.attachedTag=t}function y(t){const e=()=>p.attachedTag?t.p(p.attachedTag):t;return Object.defineProperty(t,"a",{get:e}),Object.defineProperty(t,"attach",{get:e}),t}const b={};for(let t in d)b[t]=y(d[t]);const _=new class{constructor(){this.cssGenerator=new g,this.beautifyCss=!0}generateHtml(t){return this._generateTag(t)}_generateTag(t){if("style"==t.tagName)return this._createTag(t,this.cssGenerator.generateCss(t._meta.storage));if("script"==t.tagName)return this._createTag(t,this._generateScriptContent(t._meta.storage));let e="";for(const s of t.children){let t=s;s instanceof o&&(t=s.b()),e+=t instanceof u?this._generateTag(t):s}return this._createTag(t,e)}_createTag(t,e){const s=this._generateAttributeString(t);let a=[t.tagName,s].filter((t=>t)).join(" ");return t._meta.selfClosing?`<${a}/>`:`<${a}>${e}`}_generateAttributeString(t){let e="";t.attr.id&&(e+=this._attr("id",t.attr.id)),e+=this._attr("class",t.attr.className.raw()),e+=this._generateInlineStyle(t);for(const s in t.attr.additionalAttributes)e+=this._attr(s,t.attr.additionalAttributes[s]);return e}_generateInlineStyle(t){let e=this.cssGenerator.generateInline(t.attr.style.styles);return this._attr("style",e)}_generateScriptContent(t){let e="";if(t instanceof Function)e+=function(t){let e=t.toString();return e=e.replace(/^(.*{)/,""),e=e.replace(/}$/,""),e=e.replace(/^\(.*\)\s?=>\s?{/,""),e.trim()}(t);else if(t instanceof Array)for(const s of t)e+=this._generateScriptContent(s);return e}_attr(t,e){return e?`${t}="${e}"`:""}};const N=b;t.Tag=u,t.TagBuilder=o,t._context=p,t.attach=f,t.builders=N,t.detach=function(){p.attachedTagStack.length>0?p.attachedTag=p.attachedTagStack.pop():p.attachedTag=null},t.doc=function(t="New Hobo Document",e=m.body){const s=N.head.aa("lang","en").build(N.meta.addAttr("charset","UTF-8"),N.meta.setAttr({name:"viewport",content:"width=device-width, initial-scale=1.0"}),N.title(t)),a=N.body.build(),r=N.html.build(s,a);switch(e){case m.html:f(r);break;case m.head:f(r.findByTagName("head"));break;case m.body:f(r.findByTagName("body"));case m.none:}return{doc:r,head:s,body:a}},t.generate=function(t){return _.generateHtml(t)}})); -------------------------------------------------------------------------------- /dist/hobo.umd.min.js.gz: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nombrekeff/hobo-js/1cd54a3fe83bbd15820fb2e05fb6736a35efba6b/dist/hobo.umd.min.js.gz -------------------------------------------------------------------------------- /dist/style.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a set of styles 3 | */ 4 | export class StyleSet { 5 | constructor() { 6 | this.styles = {}; 7 | } 8 | copy() { 9 | const newStyles = new StyleSet(); 10 | newStyles.styles = Object.assign({}, this.styles); 11 | return newStyles; 12 | } 13 | /** Set a single style */ 14 | set(key, value) { 15 | this.styles[key] = value; 16 | return this; 17 | } 18 | /** Remove styles */ 19 | remove(...styles) { 20 | for (const sn of styles) { 21 | delete this.styles[sn]; 22 | } 23 | return this; 24 | } 25 | /** Check if a style is set */ 26 | has(key) { 27 | return key in this.styles; 28 | } 29 | } 30 | //# sourceMappingURL=style.js.map -------------------------------------------------------------------------------- /dist/style.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"style.js","sourceRoot":"","sources":["../src/style.ts"],"names":[],"mappings":"AAGA;;GAEG;AACH,MAAM,OAAO,QAAQ;IAArB;QACE,WAAM,GAAuD,EAAE,CAAC;IA0BlE,CAAC;IAxBC,IAAI;QACF,MAAM,SAAS,GAAG,IAAI,QAAQ,EAAE,CAAC;QACjC,SAAS,CAAC,MAAM,qBAAQ,IAAI,CAAC,MAAM,CAAE,CAAC;QACtC,OAAO,SAAS,CAAC;IACnB,CAAC;IAED,yBAAyB;IACzB,GAAG,CAAwB,GAAM,EAAE,KAA4B;QAC7D,IAAI,CAAC,MAAM,CAAC,GAAG,CAAC,GAAG,KAAY,CAAC;QAChC,OAAO,IAAI,CAAC;IACd,CAAC;IAED,oBAAoB;IACpB,MAAM,CAAC,GAAG,MAAgB;QACxB,KAAK,MAAM,EAAE,IAAI,MAAM,EAAE;YACvB,OAAO,IAAI,CAAC,MAAM,CAAC,EAAE,CAAC,CAAC;SACxB;QACD,OAAO,IAAI,CAAC;IACd,CAAC;IAED,8BAA8B;IAC9B,GAAG,CAAC,GAAW;QACb,OAAO,GAAG,IAAI,IAAI,CAAC,MAAM,CAAC;IAC5B,CAAC;CACF"} -------------------------------------------------------------------------------- /dist/tag-builder.js: -------------------------------------------------------------------------------- 1 | import { AttrSet } from './attributes'; 2 | import { Tag } from './tag'; 3 | import { allKnownTags, selfClosingTags, storableTags } from './custom-types/tag-names'; 4 | class ExFunc extends Function { 5 | constructor() { 6 | super('...args', 'return this.__self__.__call__(...args)'); 7 | var self = this.bind(this); 8 | this.__self__ = self; 9 | return self; 10 | } 11 | /* istanbul ignore next */ 12 | __call__(...children) { } 13 | } 14 | /** 15 | * TagBuilder class, used to build tags of course. 16 | */ 17 | export class TagBuilder extends ExFunc { 18 | /** Get the tag className */ 19 | get className() { 20 | return this.attr.className; 21 | } 22 | /** Get the tag id */ 23 | get tagId() { 24 | return this.attr.id; 25 | } 26 | constructor(tagName, ...children) { 27 | super(); 28 | this.children = []; 29 | /** 30 | * Do not modify directly, use helper methods in the tag instead. 31 | */ 32 | this.attr = new AttrSet(); 33 | this._meta = { 34 | selfClosing: false, 35 | storesChildren: false, 36 | storage: false, 37 | }; 38 | this.setTagName(tagName); 39 | this.children.push(...children); 40 | } 41 | copy() { 42 | const newBuilder = new TagBuilder(this.tagName, ...this.children); 43 | newBuilder.attr = this.attr.copy(); 44 | newBuilder._parent = this._parent; 45 | newBuilder._meta = Object.assign(Object.assign({}, this._meta), newBuilder._meta); 46 | return newBuilder; 47 | } 48 | /** Sets and validates the tag name */ 49 | setTagName(name) { 50 | this.tagName = this.sanitizeTagName(name); 51 | this.validateTagName(this.tagName); 52 | this._meta = Object.assign(Object.assign({}, this._meta), this.getMetaForTag(this.tagName)); 53 | return this; 54 | } 55 | /** 56 | * Shorthand for `.build` method 57 | * Build the tag with additional children 58 | */ 59 | b(...children) { 60 | return this.build(...children); 61 | } 62 | /** 63 | * Build the tag with additional children 64 | */ 65 | build(...children) { 66 | return this.__call__(...children); 67 | } 68 | __call__(...children) { 69 | let tagChildren = [...this.children, ...children]; 70 | if (this._meta.storesChildren) { 71 | this._meta.storage = children; 72 | tagChildren = []; 73 | } 74 | const built = new Tag(this.tagName, tagChildren, this.attr, this._meta); 75 | if (this._parent) { 76 | this._parent.children.push(built); 77 | } 78 | return built; 79 | } 80 | /** Attach to the currently attached tag in the global hobo context */ 81 | /* istanbul ignore next */ 82 | get a() { 83 | return this.copy(); 84 | } 85 | /** Same as .a - Attach to the currently attached tag in the global hobo context */ 86 | /* istanbul ignore next */ 87 | get attach() { 88 | return this.copy(); 89 | } 90 | /** Set the parent of the tag. If a parent is set, this tag will be added as a child when built */ 91 | p(parent) { 92 | const copy = this.copy(); 93 | copy._parent = parent; 94 | return copy; 95 | } 96 | /** 97 | * Set the id of the tag 98 | * Can't be empty 99 | */ 100 | id(newId) { 101 | const copy = this.copy(); 102 | copy.attr.id = newId; 103 | return copy; 104 | } 105 | /** replaces the children of this tag with the provided string */ 106 | text(content) { 107 | const copy = this.copy(); 108 | copy.children = [content]; 109 | return copy; 110 | } 111 | /** 112 | * Adds tags as children if the tag can have children. 113 | * For example, if tag is `img` there's no need to add the childre as they will not be generated. 114 | */ 115 | append(...tags) { 116 | if (this._meta.selfClosing) { 117 | return this; 118 | } 119 | const copy = this.copy(); 120 | copy.children.push(...tags.map((c) => (c instanceof TagBuilder ? c.b() : c))); 121 | return copy; 122 | } 123 | /** Set the children of this tag. Replaces any current children */ 124 | setChildren(children) { 125 | const copy = this.copy(); 126 | copy.children = children; 127 | return copy; 128 | } 129 | /** Store metadata inside tag. Internal method, you won't need this */ 130 | store(o) { 131 | const copy = this.copy(); 132 | copy._meta.storage = o; 133 | return copy; 134 | } 135 | /** 136 | * cm = modify 137 | * calls `fn` with the tag, and returns the tag 138 | * 139 | * usefull to change a tag while maintaing chaning 140 | * 141 | * @example 142 | * ```ts 143 | * div().m(t => t.className.add("Container")) 144 | * .div("I'm a child!"), 145 | * ``` 146 | * @example 147 | * ```ts 148 | * div([ 149 | * p("Child1").m(t => t.className.add("child-1")), 150 | * p("Child1").m(t => t.className.add("child-2")) 151 | * ]) 152 | * ``` 153 | */ 154 | m(fn) { 155 | return this.mod(fn); 156 | } 157 | /** 158 | * calls `fn` with the tag, and returns the tag 159 | * 160 | * usefull to change a tag while maintaing chaning 161 | * 162 | * @example 163 | * ```ts 164 | * div().m(t => t.className.add("Container")) 165 | * .div("I'm a child!"), 166 | * ``` 167 | * @example 168 | * ```ts 169 | * div([ 170 | * p("Child1").mod(t => t.className.add("child-1")), 171 | * p("Child1").mod(t => t.className.add("child-2")) 172 | * ]) 173 | * ``` 174 | */ 175 | mod(fn) { 176 | const copy = this.copy(); 177 | fn(copy); 178 | return copy; 179 | } 180 | /** 181 | * Shortcut for method .modClass 182 | * 183 | * Modifies the classnames of a tag. Similar to the `.mod` or `.m` methods 184 | * but it passes the className instead of the complete tag. 185 | * 186 | * Retuns a new TagBuilder 187 | */ 188 | mc(arg0) { 189 | return this.modClass(arg0); 190 | } 191 | /** 192 | * Modifies the classnames of a tag. Similar to the `.mod` or `.m` methods 193 | * but it passes the className instead of the complete tag. 194 | * 195 | * Retuns a new TagBuilder 196 | * 197 | * @example 198 | * ```ts 199 | * div( 200 | * p("Child1").modClass(c => c.add("child-1")), 201 | * p("Child1").modClass(c => c.add("child-2")) 202 | * ) 203 | * ``` 204 | */ 205 | modClass(arg0) { 206 | const copy = this.copy(); 207 | return copy.m((t) => arg0(t.className)); 208 | } 209 | /** 210 | * Shorthand for .addClass method. 211 | * Adds classNames to this TagBuilder, and returns a new TagBuilder 212 | */ 213 | ac(...classNames) { 214 | return this.addClass(...classNames); 215 | } 216 | /** 217 | * Adds classNames to this TagBuilder, and returns a new TagBuilder 218 | */ 219 | addClass(...classNames) { 220 | const copy = this.copy(); 221 | copy.className.add(...classNames); 222 | return copy; 223 | } 224 | /** 225 | * Shorthand for .rmClass method. 226 | * Removes classNames from this TagBuilder, and returns a new TagBuilder 227 | */ 228 | rc(...classNames) { 229 | return this.rmClass(...classNames); 230 | } 231 | /** 232 | * Removes classNames from this TagBuilder, and returns a new TagBuilder 233 | */ 234 | rmClass(...classNames) { 235 | const copy = this.copy(); 236 | copy.className.remove(...classNames); 237 | return copy; 238 | } 239 | /** 240 | * Shorthand for .addAttr method. 241 | * Adds attribute, and returns a new TagBuilder 242 | */ 243 | aa(key, value) { 244 | return this.addAttr(key, value); 245 | } 246 | /** Add one attribute, and return a new TagBuilder */ 247 | addAttr(key, value) { 248 | const copy = this.copy(); 249 | copy.attr.set(key, value); 250 | return copy; 251 | } 252 | /** 253 | * Shorthand for .setAttr method. 254 | * Sets multiple atributes at once, and returns a new TagBuilder 255 | */ 256 | sa(attributes) { 257 | return this.setAttr(attributes); 258 | } 259 | /** Sets multiple atributes at once, and returns a new TagBuilder */ 260 | setAttr(attributes) { 261 | const copy = this.copy(); 262 | copy.attr.additionalAttributes = Object.assign(Object.assign({}, copy.attr.additionalAttributes), attributes); 263 | return copy; 264 | } 265 | /** 266 | * Shorthand for .removeAttr method. 267 | * Removes attribute from this TagBuilder, and returns a new TagBuilder 268 | */ 269 | ra(...attr) { 270 | return this.rmAttr(...attr); 271 | } 272 | /** 273 | * Removes attribute from this TagBuilder, and returns a new TagBuilder 274 | */ 275 | rmAttr(...attr) { 276 | const copy = this.copy(); 277 | copy.attr.remove(...attr); 278 | return copy; 279 | } 280 | /** 281 | * Shorthand for .addStyle method 282 | * Adds a single style, and returns a new TagBuilder 283 | */ 284 | as(key, value) { 285 | return this.addStyle(key, value); 286 | } 287 | /** 288 | * Adds a single style, and returns a new TagBuilder 289 | */ 290 | addStyle(key, value) { 291 | const copy = this.copy(); 292 | copy.attr.style.set(key, value); 293 | return copy; 294 | } 295 | /** 296 | * Shorthand for .setStyles method. 297 | * Adds style from object, and returns a new TagBuilder 298 | */ 299 | ss(styles) { 300 | return this.setStyles(styles); 301 | } 302 | /** Adds style from object, and returns a new TagBuilder */ 303 | setStyles(styles) { 304 | const copy = this.copy(); 305 | copy.attr.style.styles = Object.assign(Object.assign({}, copy.attr.style.styles), styles); 306 | return copy; 307 | } 308 | /** 309 | * Shorthand for .removeStyles method. 310 | * Removes styles from this TagBuilder, and returns a new TagBuilder 311 | */ 312 | rs(...styleNames) { 313 | return this.rmStyle(...styleNames); 314 | } 315 | /** 316 | * Removes styles from this TagBuilder, and returns a new TagBuilder 317 | */ 318 | rmStyle(...styleNames) { 319 | const copy = this.copy(); 320 | copy.attr.style.remove(...styleNames); 321 | return copy; 322 | } 323 | // Utilities 324 | getMetaForTag(tagName) { 325 | return { 326 | selfClosing: this.isSelfClosingTag(tagName), 327 | storesChildren: this.isStorableTag(tagName), 328 | storage: null, 329 | }; 330 | } 331 | isSelfClosingTag(tagName) { 332 | return selfClosingTags.includes(tagName); 333 | } 334 | isStorableTag(tagName) { 335 | return storableTags.includes(tagName); 336 | } 337 | validateTagName(tagName) { 338 | if (!/[a-zA-Z_][a-z-A-Z0-9_]*/.test(tagName)) { 339 | throw new Error(`Invalid tag name "${tagName}"`); 340 | } 341 | } 342 | sanitizeTagName(tagName) { 343 | return tagName.replace(/[^\w\d-_]/, ''); 344 | } 345 | } 346 | function tagBuilder(tagName, ...children) { 347 | return new TagBuilder(tagName, ...children); 348 | } 349 | const tagNames = allKnownTags; 350 | let fns = { 351 | tag: tagBuilder, 352 | }; 353 | for (let tname of tagNames) { 354 | fns[tname] = tagBuilder(tname); 355 | } 356 | export const builders = fns; 357 | //# sourceMappingURL=tag-builder.js.map -------------------------------------------------------------------------------- /dist/tag-builder.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag-builder.js","sourceRoot":"","sources":["../src/tag-builder.ts"],"names":[],"mappings":"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"} -------------------------------------------------------------------------------- /dist/tag.js: -------------------------------------------------------------------------------- 1 | import { AttrSet } from './attributes'; 2 | import { TagBuilder } from './tag-builder'; 3 | /** 4 | * Represents an html tag 5 | */ 6 | export class Tag { 7 | get className() { 8 | return this.attr.className; 9 | } 10 | get tagId() { 11 | return this.attr.id; 12 | } 13 | constructor(tagName, children, attr, meta) { 14 | this.children = []; 15 | this.attr = new AttrSet(); 16 | this._meta = { 17 | selfClosing: false, 18 | storesChildren: false, 19 | storage: false, 20 | }; 21 | this.tagName = tagName; 22 | this.children = children; 23 | this.attr = attr; 24 | this._meta = meta; 25 | } 26 | /** Append children */ 27 | append(child) { 28 | if (child instanceof TagBuilder) 29 | child = child.b(); 30 | this.children.push(child); 31 | } 32 | /** Find a child by tag name */ 33 | findByTagName(targetTagName) { 34 | return this.findOneBy((t) => t.tagName == targetTagName); 35 | } 36 | /** Find a child by custom test */ 37 | findOneBy(test) { 38 | const stack = []; 39 | stack.push(this); 40 | while (stack.length > 0) { 41 | let tag = stack.pop(); 42 | if (test(tag)) { 43 | return tag; 44 | } 45 | else if (tag.children && tag.children.length) { 46 | for (let ii = 0; ii < tag.children.length; ii += 1) { 47 | if (this.children[ii] instanceof Tag) { 48 | stack.push(tag.children[ii]); 49 | } 50 | } 51 | } 52 | } 53 | return null; 54 | } 55 | } 56 | //# sourceMappingURL=tag.js.map -------------------------------------------------------------------------------- /dist/tag.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"tag.js","sourceRoot":"","sources":["../src/tag.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,OAAO,EAAE,MAAM,cAAc,CAAC;AAEvC,OAAO,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAE3C;;GAEG;AACH,MAAM,OAAO,GAAG;IAWd,IAAI,SAAS;QACX,OAAO,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC;IAC7B,CAAC;IACD,IAAI,KAAK;QACP,OAAO,IAAI,CAAC,IAAI,CAAC,EAAE,CAAC;IACtB,CAAC;IAED,YAAY,OAAgB,EAAE,QAAyB,EAAE,IAAa,EAAE,IAAa;QAhBrF,aAAQ,GAAoB,EAAE,CAAC;QAC/B,SAAI,GAAY,IAAI,OAAO,EAAE,CAAC;QAE9B,UAAK,GAAY;YACf,WAAW,EAAE,KAAK;YAClB,cAAc,EAAE,KAAK;YACrB,OAAO,EAAE,KAAK;SACf,CAAC;QAUA,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC;QACvB,IAAI,CAAC,QAAQ,GAAG,QAAQ,CAAC;QACzB,IAAI,CAAC,IAAI,GAAG,IAAI,CAAC;QACjB,IAAI,CAAC,KAAK,GAAG,IAAI,CAAC;IACpB,CAAC;IAED,uBAAuB;IACvB,MAAM,CAAC,KAAoB;QACzB,IAAI,KAAK,YAAY,UAAU;YAAE,KAAK,GAAG,KAAK,CAAC,CAAC,EAAE,CAAC;QACnD,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAC5B,CAAC;IAED,gCAAgC;IAChC,aAAa,CAAC,aAAsB;QAClC,OAAO,IAAI,CAAC,SAAS,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,OAAO,IAAI,aAAa,CAAC,CAAC;IAC3D,CAAC;IAED,mCAAmC;IACnC,SAAS,CAAC,IAAY;QACpB,MAAM,KAAK,GAAU,EAAE,CAAC;QACxB,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAEjB,OAAO,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE;YACvB,IAAI,GAAG,GAAQ,KAAK,CAAC,GAAG,EAAS,CAAC;YAElC,IAAI,IAAI,CAAC,GAAG,CAAC,EAAE;gBACb,OAAO,GAAG,CAAC;aACZ;iBAAM,IAAI,GAAG,CAAC,QAAQ,IAAI,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE;gBAC9C,KAAK,IAAI,EAAE,GAAG,CAAC,EAAE,EAAE,GAAG,GAAG,CAAC,QAAQ,CAAC,MAAM,EAAE,EAAE,IAAI,CAAC,EAAE;oBAClD,IAAI,IAAI,CAAC,QAAQ,CAAC,EAAE,CAAC,YAAY,GAAG,EAAE;wBACpC,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,QAAQ,CAAC,EAAE,CAAQ,CAAC,CAAC;qBACrC;iBACF;aACF;SACF;QAED,OAAO,IAAI,CAAC;IACd,CAAC;CACF"} -------------------------------------------------------------------------------- /dist/types/attributes.d.ts: -------------------------------------------------------------------------------- 1 | import { ClassName } from './class-name'; 2 | import { StyleSet } from './style'; 3 | /** 4 | * Represents a tag's attribute set. 5 | */ 6 | export declare class AttrSet { 7 | className: ClassName; 8 | id?: string; 9 | style: StyleSet; 10 | additionalAttributes: { 11 | [key: string]: string; 12 | }; 13 | copy(): AttrSet; 14 | /** Set single attribute */ 15 | set(key: string, value: string): AttrSet; 16 | /** Remove attributes */ 17 | remove(...attrs: string[]): AttrSet; 18 | /** Check if an attribute is set */ 19 | has(key: string): boolean; 20 | } 21 | -------------------------------------------------------------------------------- /dist/types/class-name.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a tag's class list. 3 | */ 4 | export declare class ClassName { 5 | classNames: string[]; 6 | constructor(classNames?: string[]); 7 | copy(): ClassName; 8 | /** 9 | * Returns a string of all the class names separated by spaces. 10 | */ 11 | raw(): string; 12 | /** Add one or more class names */ 13 | add(...classNames: string[]): ClassName; 14 | /** Remove one or more class names */ 15 | remove(...classNames: string[]): ClassName; 16 | /** Check if a class name is present. */ 17 | has(str: string): boolean; 18 | } 19 | -------------------------------------------------------------------------------- /dist/types/custom-types/colors.d.ts: -------------------------------------------------------------------------------- 1 | export type NamedColor = 'black' | 'silver' | 'gray' | 'white' | 'maroon' | 'red' | 'purple' | 'fuchsia' | 'green' | 'lime' | 'olive' | 'yellow' | 'navy' | 'blue' | 'teal' | 'aqua' | 'aliceblue' | 'antiquewhite' | 'aqua' | 'aquamarine' | 'azure' | 'beige' | 'bisque' | 'black' | 'blanchedalmond' | 'blue' | 'blueviolet' | 'brown' | 'burlywood' | 'cadetblue' | 'chartreuse' | 'chocolate' | 'coral' | 'cornflowerblue' | 'cornsilk' | 'crimson' | 'cyan' | 'darkblue' | 'darkcyan' | 'darkgoldenrod' | 'darkgray' | 'darkgreen' | 'darkgrey' | 'darkkhaki' | 'darkmagenta' | 'darkolivegreen' | 'darkorange' | 'darkorchid' | 'darkred' | 'darksalmon' | 'darkseagreen' | 'darkslateblue' | 'darkslategray' | 'darkslategrey' | 'darkturquoise' | 'darkviolet' | 'deeppink' | 'deepskyblue' | 'dimgray' | 'dimgrey' | 'dodgerblue' | 'firebrick' | 'floralwhite' | 'forestgreen' | 'fuchsia' | 'gainsboro' | 'ghostwhite' | 'gold' | 'goldenrod' | 'gray' | 'green' | 'greenyellow' | 'grey' | 'honeydew' | 'hotpink' | 'indianred' | 'indigo' | 'ivory' | 'khaki' | 'lavender' | 'lavenderblush' | 'lawngreen' | 'lemonchiffon' | 'lightblue' | 'lightcoral' | 'lightcyan' | 'lightgoldenrodyellow' | 'lightgray' | 'lightgreen' | 'lightgrey' | 'lightpink' | 'lightsalmon' | 'lightseagreen' | 'lightskyblue' | 'lightslategray' | 'lightslategrey' | 'lightsteelblue' | 'lightyellow' | 'lime' | 'limegreen' | 'linen' | 'magenta' | 'maroon' | 'mediumaquamarine' | 'mediumblue' | 'mediumorchid' | 'mediumpurple' | 'mediumseagreen' | 'mediumslateblue' | 'mediumspringgreen' | 'mediumturquoise' | 'mediumvioletred' | 'midnightblue' | 'mintcream' | 'mistyrose' | 'moccasin' | 'navajowhite' | 'navy' | 'oldlace' | 'olive' | 'olivedrab' | 'orange' | 'orangered' | 'orchid' | 'palegoldenrod' | 'palegreen' | 'paleturquoise' | 'palevioletred' | 'papayawhip' | 'peachpuff' | 'peru' | 'pink' | 'plum' | 'powderblue' | 'purple' | 'rebeccapurple' | 'red' | 'rosybrown' | 'royalblue' | 'saddlebrown' | 'salmon' | 'sandybrown' | 'seagreen' | 'seashell' | 'sienna' | 'silver' | 'skyblue' | 'slateblue' | 'slategray' | 'slategrey' | 'snow' | 'springgreen' | 'steelblue' | 'tan' | 'teal' | 'thistle' | 'tomato' | 'turquoise' | 'violet' | 'wheat' | 'white' | 'whitesmoke' | 'yellow' | 'yellowgreen'; 2 | -------------------------------------------------------------------------------- /dist/types/custom-types/css-properties.d.ts: -------------------------------------------------------------------------------- 1 | export type CssProperty = 'color' | 'border' | 'margin' | 'fontStyle' | 'transform' | 'backgroundColor' | 'alignContent' | 'alignItems' | 'alignSelf' | 'all' | 'animation' | 'animationDelay' | 'animationDirection' | 'animationDuration' | 'animationFillMode' | 'animationIterationCount' | 'animationName' | 'animationPlayState' | 'animationTimingFunction' | 'backfaceVisibility' | 'background' | 'backgroundAttachment' | 'backgroundBlendMode' | 'backgroundClip' | 'backgroundColor' | 'backgroundImage' | 'backgroundOrigin' | 'backgroundPosition' | 'backgroundRepeat' | 'backgroundSize' | 'border' | 'borderBottom' | 'borderBottomColor' | 'borderBottomLeftRadius' | 'borderBottomRightRadius' | 'borderBottomStyle' | 'borderBottomWidth' | 'borderCollapse' | 'borderColor' | 'borderImage' | 'borderImageOutset' | 'borderImageRepeat' | 'borderImageSlice' | 'borderImageSource' | 'borderImageWidth' | 'borderLeft' | 'borderLeftColor' | 'borderLeftStyle' | 'borderLeftWidth' | 'borderRadius' | 'borderRight' | 'borderRightColor' | 'borderRightStyle' | 'borderRightWidth' | 'borderSpacing' | 'borderStyle' | 'borderTop' | 'borderTopColor' | 'borderTopLeftRadius' | 'borderTopRightRadius' | 'borderTopStyle' | 'borderTopWidth' | 'borderWidth' | 'bottom' | 'boxShadow' | 'boxSizing' | 'captionSide' | 'caretColor' | '@charset' | 'clear' | 'clip' | 'clipPath' | 'color' | 'columnCount' | 'columnFill' | 'columnGap' | 'columnRule' | 'columnRuleColor' | 'columnRuleStyle' | 'columnRuleWidth' | 'columnSpan' | 'columnWidth' | 'columns' | 'content' | 'counterIncrement' | 'counterReset' | 'cursor' | 'direction' | 'display' | 'emptyCells' | 'filter' | 'flex' | 'flexBasis' | 'flexDirection' | 'flexFlow' | 'flexGrow' | 'flexShrink' | 'flexWrap' | 'float' | 'font' | '@fontFace' | 'fontFamily' | 'fontKerning' | 'fontSize' | 'fontSizeAdjust' | 'fontStretch' | 'fontStyle' | 'fontVariant' | 'fontWeight' | 'grid' | 'gridArea' | 'gridAutoColumns' | 'gridAutoFlow' | 'gridAutoRows' | 'gridColumn' | 'gridColumnEnd' | 'gridColumnGap' | 'gridColumnStart' | 'gridGap' | 'gridRow' | 'gridRowEnd' | 'gridRowGap' | 'gridRowStart' | 'gridTemplate' | 'gridTemplateAreas' | 'gridTemplateColumns' | 'gridTemplateRows' | 'height' | 'hyphens' | '@import' | 'justifyContent' | '@keyframes' | 'left' | 'letterSpacing' | 'lineHeight' | 'listStyle' | 'listStyleImage' | 'listStylePosition' | 'listStyleType' | 'margin' | 'marginBottom' | 'marginLeft' | 'marginRight' | 'marginTop' | 'maxHeight' | 'maxWidth' | '@media' | 'minHeight' | 'minWidth' | 'objectFit' | 'objectPosition' | 'opacity' | 'order' | 'outline' | 'outlineColor' | 'outlineOffset' | 'outlineStyle' | 'outlineWidth' | 'overflow' | 'overflowX' | 'overflowY' | 'padding' | 'paddingBottom' | 'paddingLeft' | 'paddingRight' | 'paddingTop' | 'pageBreakAfter' | 'pageBreakBefore' | 'pageBreakInside' | 'perspective' | 'perspectiveOrigin' | 'pointerEvents' | 'position' | 'quotes' | 'right' | 'scrollBehavior' | 'tableLayout' | 'textAlign' | 'textAlignLast' | 'textDecoration' | 'textDecorationColor' | 'textDecorationLine' | 'textDecorationStyle' | 'textIndent' | 'textJustify' | 'textOverflow' | 'textShadow' | 'textTransform' | 'top' | 'transform' | 'transformOrigin' | 'transformStyle' | 'transition' | 'transitionDelay' | 'transitionDuration' | 'transitionProperty' | 'transitionTimingFunction' | 'userSelect' | 'verticalAlign' | 'visibility' | 'whiteSpace' | 'width' | 'wordBreak' | 'wordSpacing' | 'wordWrap' | 'writingMode' | 'zIndex' | (string & {}); 2 | -------------------------------------------------------------------------------- /dist/types/custom-types/css-property-values.d.ts: -------------------------------------------------------------------------------- 1 | import { NamedColor } from './colors'; 2 | export type PickPropertyValues = T extends 'color' ? ColorOptions : T extends 'alignContent' ? AlignContentOptions : T extends 'alignItems' ? AlignItemsOptions : T extends 'alignSelf' ? AlignSelfOptions : T extends 'all' ? AllOptions : T extends 'accentColor' ? ColorOptions : T extends 'animationDirection' ? AnimationDirectionOptions : T extends 'animationFillMode' ? AnimationFillModeOptions : T extends 'animationPlayState' ? AnimationPlayStateOptions : T extends 'animationPlayState' ? AnimationPlayStateOptions : T extends 'borderStyle' ? BorderStyleOptions : T extends 'background' ? ColorOptions : T extends 'backgroundColor' ? ColorOptions : T extends 'backgroundImage' ? BackgroundImageOptions : T extends 'backgroundRepeat' ? BackgroundRepeatOptions : T extends 'backgroundAttachment' ? BackgroundAttachmentOptions : T extends 'backgroundPosition' ? BackgroundPositionOptions : T extends 'position' ? PPositionOptions : T extends 'transform' ? TransformOptions : T extends 'fontStyle' ? FontStyleOptions : T extends 'fontWeight' ? FontWeightOptions : T extends 'flexDirection' ? FlexDirectionOptions : T extends 'zIndex' ? Number : T extends 'top' ? Number : T extends 'display' ? DisplayOptions : T extends 'bottom' ? Number : T extends 'left' ? Number : T extends 'right' ? Number : string & {}; 3 | export type CommonOptions = 'initial' | 'inherit' | (string & {}); 4 | export type ColorOptions = NamedColor | (string & {}); 5 | export type AlignContentOptions = 'flex-wrap' | 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'space-between' | 'space-around' | CommonOptions; 6 | export type AlignItemsOptions = 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'baseline' | CommonOptions; 7 | export type DisplayOptions = 'inline' | 'block' | 'contents' | 'flex' | 'grid' | 'inline-block' | 'inline-flex' | 'inline-grid' | 'inline-table' | 'list-item' | 'run-in' | 'table' | 'table-caption' | 'table-column-group' | 'table-header-group' | 'table-footer-group' | 'table-row-group' | 'table-cell' | 'table-column' | 'table-row' | 'none' | CommonOptions; 8 | type AlignSelfOptions = 'auto' | 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'baseline' | CommonOptions; 9 | type AllOptions = CommonOptions | 'unset'; 10 | type AnimationDirectionOptions = 'normal' | 'reverse' | 'alternate' | 'alternate-reverse' | 'initial' | 'inherit' | (string & {}); 11 | type AnimationFillModeOptions = 'none' | 'forwards' | 'backwards' | 'both' | CommonOptions; 12 | type AnimationPlayStateOptions = 'paused' | 'running' | CommonOptions; 13 | type BorderStyleOptions = 'none' | 'no' | 'none' | 'hidden' | 'dotted' | 'dashed' | 'solid' | 'double' | 'groove' | 'ridge' | 'inset' | 'outset' | CommonOptions; 14 | export type BackgroundImageOptions = 'url()' | 'none' | 'conic-gradient()' | 'linear-gradient()' | 'radial-gradient()' | 'repeating-conic-gradient()' | 'repeating-linear-gradient()' | 'repeating-radial-gradient()' | 'initial' | 'inherit'; 15 | type BackgroundRepeatOptions = 'repeat' | 'no' | 'repeat' | 'repeat-x' | 'repeat-y' | 'no-repeat' | 'space' | 'round' | CommonOptions; 16 | type BackgroundAttachmentOptions = 'scroll' | 'no' | 'scroll' | 'fixed' | 'local' | CommonOptions; 17 | type BackgroundPositionOptions = 'left top' | 'left center' | 'left bottom' | 'right top' | 'right center' | 'right bottom' | 'center top' | 'center center' | 'center bottom' | 'inherit' | 'initial' | (string & {}); 18 | export type PPositionOptions = 'static' | 'fixed' | 'absolute' | 'relative' | 'sticky' | CommonOptions; 19 | export type TransformOptions = 'none' | 'matrix(n,n,n,n,n,n)' | 'matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n)' | 'translate(x,y)' | 'translate3d(x,y,z)' | 'translateX(x)' | 'translateY(y)' | 'translateZ(z)' | 'scale(x,y)' | 'scale3d(x,y,z)' | 'scaleX(x)' | 'scaleY(y)' | 'scaleZ(z)' | 'rotate(angle)' | 'rotate3d(x,y,z,angle)' | 'rotateX(angle)' | 'rotateY(angle)' | 'rotateZ(angle)' | 'skew(x-angle,y-angle)' | 'skewX(angle)' | 'skewY(angle)' | 'perspective(n)' | CommonOptions; 20 | export type FontWeightOptions = 'normal' | 'bold' | 'bolder' | 'lighter' | '100' | '200' | '300' | '400' | '500' | '600' | '700' | '800' | '900' | CommonOptions; 21 | export type FontStyleOptions = 'normal' | 'italic' | 'oblique' | CommonOptions; 22 | export type FlexDirectionOptions = 'row' | 'column' | 'row-reverse' | 'column-reverse' | CommonOptions; 23 | export {}; 24 | -------------------------------------------------------------------------------- /dist/types/custom-types/tag-names.d.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @export 3 | * List of all known self-closing HTML tags 4 | */ 5 | export declare const selfClosingTags: string[]; 6 | /** List of all known closing HTML tags */ 7 | export declare const closingTags: string[]; 8 | /** List of all known HTML tags */ 9 | export declare const allKnownTags: string[]; 10 | export declare const storableTags: string[]; 11 | /** @export @type {TagName} */ 12 | export type TagName = ValidTagName | (string & {}); 13 | /** @type {ValidTagName} */ 14 | export type ValidTagName = 'a' | 'abbr' | 'acronym' | 'address' | 'area' | 'article' | 'aside' | 'audio' | 'b' | 'base' | 'basefont' | 'bdi' | 'bdo' | 'big' | 'blockquote' | 'body' | 'br' | 'button' | 'canvas' | 'caption' | 'center' | 'cite' | 'code' | 'col' | 'colgroup' | 'data' | 'datalist' | 'dd' | 'del' | 'details' | 'dfn' | 'dialog' | 'div' | 'dl' | 'dt' | 'em' | 'embed' | 'fieldset' | 'figcaption' | 'figure' | 'footer' | 'form' | 'h1' | 'h2' | 'h3' | 'h4' | 'h5' | 'h6' | 'head' | 'header' | 'hr' | 'html' | 'i' | 'iframe' | 'img' | 'input' | 'ins' | 'kbd' | 'label' | 'legend' | 'li' | 'link' | 'main' | 'map' | 'mark' | 'meta' | 'meter' | 'nav' | 'noscript' | 'object' | 'ol' | 'optgroup' | 'option' | 'output' | 'p' | 'param' | 'picture' | 'pre' | 'progress' | 'q' | 'rp' | 'rt' | 'ruby' | 's' | 'samp' | 'script' | 'section' | 'select' | 'selfClosingTagName' | 'small' | 'span' | 'strong' | 'style' | 'sub' | 'summary' | 'sup' | 'svg' | 'table' | 'tbody' | 'td' | 'template' | 'textarea' | 'tfoot' | 'th' | 'thead' | 'time' | 'title' | 'track' | 'tr' | 'u' | 'ul' | 'var' | 'video' | 'wbr'; 15 | -------------------------------------------------------------------------------- /dist/types/custom-types/types.d.ts: -------------------------------------------------------------------------------- 1 | import { Tag } from '../tag'; 2 | import { TagBuilder } from '../tag-builder'; 3 | import { CssProperty } from './css-properties'; 4 | import { PickPropertyValues } from './css-property-values'; 5 | export type StyleMap = { 6 | [key in CssProperty]?: PickPropertyValues; 7 | }; 8 | export type NestedStyleMap = { 9 | [key in CssProperty]?: PickPropertyValues | StyleMap; 10 | }; 11 | export type StyleSet = { 12 | [key: string]: NestedStyleMap; 13 | }; 14 | export declare enum AttachMode { 15 | none = 0, 16 | body = 1, 17 | html = 2, 18 | head = 3 19 | } 20 | export type ValidTagChild = string | Tag | { 21 | [key: string]: StyleMap; 22 | } | TagBuilder | ((_: string) => void); 23 | export type TagMeta = { 24 | storage: any; 25 | selfClosing: boolean; 26 | storesChildren: boolean; 27 | }; 28 | export type FindBy = (tag: Tag) => boolean; 29 | export type State = {} & {}; 30 | export type StateProxy = T & { 31 | state: boolean; 32 | }; 33 | export type GlobalStuff = Function | string; 34 | export type HoboContext = { 35 | attachedTag: Tag | undefined | null; 36 | attachedTagStack: Tag[]; 37 | globalStuff: GlobalStuff[]; 38 | }; 39 | export type HtmlEventType = 'click' | (string & {}); 40 | -------------------------------------------------------------------------------- /dist/types/generation/css-generator.d.ts: -------------------------------------------------------------------------------- 1 | import { NestedStyleMap, StyleMap } from '../custom-types/types'; 2 | export declare class CssGenerator { 3 | generateCss(styleSheet: { 4 | [key: string]: NestedStyleMap; 5 | } | { 6 | [key: string]: NestedStyleMap; 7 | }[]): string; 8 | generateBlock(selector: string, style: NestedStyleMap): string; 9 | generateBlockContent(selector: string, style: NestedStyleMap): string[]; 10 | generateInline(style: StyleMap): string; 11 | generateStyle(name: string, value: string): string; 12 | } 13 | -------------------------------------------------------------------------------- /dist/types/generation/html-generator.d.ts: -------------------------------------------------------------------------------- 1 | import { Tag } from '../tag'; 2 | export declare class HtmlGenerator { 3 | private cssGenerator; 4 | beautifyCss: boolean; 5 | /** Generate html from the tag provided */ 6 | generateHtml(rootTag: Tag): string; 7 | private _generateTag; 8 | private _createTag; 9 | private _generateAttributeString; 10 | private _generateInlineStyle; 11 | private _generateScriptContent; 12 | private _attr; 13 | } 14 | -------------------------------------------------------------------------------- /dist/types/hobo.d.ts: -------------------------------------------------------------------------------- 1 | import { Tag } from './tag'; 2 | export { Tag } from './tag'; 3 | import { AttachMode, HoboContext } from './custom-types/types'; 4 | import { TagBuilder, PickArgType } from './tag-builder'; 5 | export { TagBuilder } from './tag-builder'; 6 | import { TagName, ValidTagName } from './custom-types/tag-names'; 7 | export declare let _context: HoboContext; 8 | /** 9 | * Creates an HTML document, with a head and body tags. 10 | * You can pass in the AttachMode to attach to different tags. 11 | */ 12 | export declare function doc(pageTitle?: string, mode?: AttachMode): { 13 | doc: Tag; 14 | head: Tag; 15 | body: Tag; 16 | }; 17 | /** 18 | * Attach a given tag to the current context. 19 | * When you attach a tag, this tag will be the "root" for any tag created without a parent. 20 | * 21 | * * If there is not attached tag, it will be attached 22 | * * If there is already a tag attached, it will store the previous tag 23 | * and will set the new tag as the root. After finishing using the tag as the root, you can call `@detach` 24 | * and return to the previous root tag. 25 | * 26 | * This is used to remove clutter and reduntancies when creating hobo docs. 27 | * Like this: 28 | * 29 | * @example 30 | * Simple example with only 1 attach 31 | * ```ts 32 | * const parent = doc(); 33 | * attach(parent); 34 | * 35 | * div(); 36 | * p(); 37 | * ``` 38 | * The `div` and `p` tags will be automatically added as child of `parentDiv` 39 | * 40 | * @example 41 | * Example attaching and detaching 42 | * ```ts 43 | * const parent = doc(); 44 | * attach(parent); 45 | * 46 | * div(); 47 | * p(); 48 | * let d1 = div(); 49 | * attach(d1); 50 | * // All the p tags will be added to `d1` 51 | * p(); 52 | * p(); 53 | * p(); 54 | * // remember to call detach when you want to go back to the previous root tag 55 | * detach(); 56 | * ``` 57 | * 58 | * @param {Tag} tag 59 | */ 60 | export declare function attach(tag: Tag): void; 61 | /** 62 | * Detached the currently attached tag, and pops back to the previously attached tag. 63 | * If there are no stored tags, it will clear the attached tag. 64 | * You will need to handle the consecuent created tags. 65 | */ 66 | export declare function detach(): void; 67 | type BuilderFunctions = { 68 | [key in ValidTagName]: ((...children: PickArgType) => Tag) & TagBuilder & { 69 | a: TagBuilder; 70 | }; 71 | } & { 72 | tag: (tagName: TagName, ...children: PickArgType) => TagBuilder; 73 | }; 74 | /** Converts's the Tag tree into a html string */ 75 | export declare function generate(root: Tag): string; 76 | /** 77 | * TagBuilders for each known tag. From `div` to `acronym` 78 | */ 79 | export declare const builders: BuilderFunctions; 80 | -------------------------------------------------------------------------------- /dist/types/style.d.ts: -------------------------------------------------------------------------------- 1 | import { CssProperty } from './custom-types/css-properties'; 2 | import { PickPropertyValues } from './custom-types/css-property-values'; 3 | /** 4 | * Represents a set of styles 5 | */ 6 | export declare class StyleSet { 7 | styles: { 8 | [key in CssProperty]?: PickPropertyValues; 9 | }; 10 | copy(): StyleSet; 11 | /** Set a single style */ 12 | set(key: T, value: PickPropertyValues): StyleSet; 13 | /** Remove styles */ 14 | remove(...styles: string[]): StyleSet; 15 | /** Check if a style is set */ 16 | has(key: string): boolean; 17 | } 18 | -------------------------------------------------------------------------------- /dist/types/tag-builder.d.ts: -------------------------------------------------------------------------------- 1 | import { AttrSet } from './attributes'; 2 | import { ClassName } from './class-name'; 3 | import { Tag } from './tag'; 4 | import { CssProperty } from './custom-types/css-properties'; 5 | import { PickPropertyValues } from './custom-types/css-property-values'; 6 | import { TagName, ValidTagName } from './custom-types/tag-names'; 7 | import { StyleMap, StyleSet, TagMeta, ValidTagChild } from './custom-types/types'; 8 | declare class ExFunc extends Function { 9 | private __self__; 10 | constructor(); 11 | __call__(...children: ValidTagChild[]): void; 12 | } 13 | /** 14 | * TagBuilder class, used to build tags of course. 15 | */ 16 | export declare class TagBuilder extends ExFunc { 17 | tagName: TagName; 18 | children: ValidTagChild[]; 19 | /** Get the tag className */ 20 | get className(): ClassName; 21 | /** Get the tag id */ 22 | get tagId(): string; 23 | /** 24 | * Do not modify directly, use helper methods in the tag instead. 25 | */ 26 | attr: AttrSet; 27 | _parent: Tag; 28 | _meta: TagMeta; 29 | constructor(tagName: TagName, ...children: ValidTagChild[]); 30 | copy(): TagBuilder; 31 | /** Sets and validates the tag name */ 32 | setTagName(name: string): this; 33 | /** 34 | * Shorthand for `.build` method 35 | * Build the tag with additional children 36 | */ 37 | b(...children: ValidTagChild[]): Tag; 38 | /** 39 | * Build the tag with additional children 40 | */ 41 | build(...children: ValidTagChild[]): Tag; 42 | __call__(...children: ValidTagChild[]): Tag; 43 | /** Attach to the currently attached tag in the global hobo context */ 44 | get a(): TagBuilder; 45 | /** Same as .a - Attach to the currently attached tag in the global hobo context */ 46 | get attach(): TagBuilder; 47 | /** Set the parent of the tag. If a parent is set, this tag will be added as a child when built */ 48 | p(parent: Tag): TagBuilder; 49 | /** 50 | * Set the id of the tag 51 | * Can't be empty 52 | */ 53 | id(newId: T extends '' ? never : T): TagBuilder; 54 | /** replaces the children of this tag with the provided string */ 55 | text(content: string): TagBuilder; 56 | /** 57 | * Adds tags as children if the tag can have children. 58 | * For example, if tag is `img` there's no need to add the childre as they will not be generated. 59 | */ 60 | append(...tags: ValidTagChild[]): TagBuilder; 61 | /** Set the children of this tag. Replaces any current children */ 62 | setChildren(children: ValidTagChild[]): TagBuilder; 63 | /** Store metadata inside tag. Internal method, you won't need this */ 64 | store(o: any): TagBuilder; 65 | /** 66 | * cm = modify 67 | * calls `fn` with the tag, and returns the tag 68 | * 69 | * usefull to change a tag while maintaing chaning 70 | * 71 | * @example 72 | * ```ts 73 | * div().m(t => t.className.add("Container")) 74 | * .div("I'm a child!"), 75 | * ``` 76 | * @example 77 | * ```ts 78 | * div([ 79 | * p("Child1").m(t => t.className.add("child-1")), 80 | * p("Child1").m(t => t.className.add("child-2")) 81 | * ]) 82 | * ``` 83 | */ 84 | m(fn: (tag: TagBuilder) => void): TagBuilder; 85 | /** 86 | * calls `fn` with the tag, and returns the tag 87 | * 88 | * usefull to change a tag while maintaing chaning 89 | * 90 | * @example 91 | * ```ts 92 | * div().m(t => t.className.add("Container")) 93 | * .div("I'm a child!"), 94 | * ``` 95 | * @example 96 | * ```ts 97 | * div([ 98 | * p("Child1").mod(t => t.className.add("child-1")), 99 | * p("Child1").mod(t => t.className.add("child-2")) 100 | * ]) 101 | * ``` 102 | */ 103 | mod(fn: (tag: TagBuilder) => void): TagBuilder; 104 | /** 105 | * Shortcut for method .modClass 106 | * 107 | * Modifies the classnames of a tag. Similar to the `.mod` or `.m` methods 108 | * but it passes the className instead of the complete tag. 109 | * 110 | * Retuns a new TagBuilder 111 | */ 112 | mc(arg0: (c: ClassName) => void): TagBuilder; 113 | /** 114 | * Modifies the classnames of a tag. Similar to the `.mod` or `.m` methods 115 | * but it passes the className instead of the complete tag. 116 | * 117 | * Retuns a new TagBuilder 118 | * 119 | * @example 120 | * ```ts 121 | * div( 122 | * p("Child1").modClass(c => c.add("child-1")), 123 | * p("Child1").modClass(c => c.add("child-2")) 124 | * ) 125 | * ``` 126 | */ 127 | modClass(arg0: (c: ClassName) => void): TagBuilder; 128 | /** 129 | * Shorthand for .addClass method. 130 | * Adds classNames to this TagBuilder, and returns a new TagBuilder 131 | */ 132 | ac(...classNames: string[]): TagBuilder; 133 | /** 134 | * Adds classNames to this TagBuilder, and returns a new TagBuilder 135 | */ 136 | addClass(...classNames: string[]): TagBuilder; 137 | /** 138 | * Shorthand for .rmClass method. 139 | * Removes classNames from this TagBuilder, and returns a new TagBuilder 140 | */ 141 | rc(...classNames: string[]): TagBuilder; 142 | /** 143 | * Removes classNames from this TagBuilder, and returns a new TagBuilder 144 | */ 145 | rmClass(...classNames: string[]): TagBuilder; 146 | /** 147 | * Shorthand for .addAttr method. 148 | * Adds attribute, and returns a new TagBuilder 149 | */ 150 | aa(key: string, value: string): TagBuilder; 151 | /** Add one attribute, and return a new TagBuilder */ 152 | addAttr(key: string, value: string): TagBuilder; 153 | /** 154 | * Shorthand for .setAttr method. 155 | * Sets multiple atributes at once, and returns a new TagBuilder 156 | */ 157 | sa(attributes: { 158 | [key: string]: string; 159 | }): TagBuilder; 160 | /** Sets multiple atributes at once, and returns a new TagBuilder */ 161 | setAttr(attributes: { 162 | [key: string]: string; 163 | }): TagBuilder; 164 | /** 165 | * Shorthand for .removeAttr method. 166 | * Removes attribute from this TagBuilder, and returns a new TagBuilder 167 | */ 168 | ra(...attr: string[]): TagBuilder; 169 | /** 170 | * Removes attribute from this TagBuilder, and returns a new TagBuilder 171 | */ 172 | rmAttr(...attr: string[]): TagBuilder; 173 | /** 174 | * Shorthand for .addStyle method 175 | * Adds a single style, and returns a new TagBuilder 176 | */ 177 | as(key: T, value: PickPropertyValues): TagBuilder; 178 | /** 179 | * Adds a single style, and returns a new TagBuilder 180 | */ 181 | addStyle(key: T, value: PickPropertyValues): TagBuilder; 182 | /** 183 | * Shorthand for .setStyles method. 184 | * Adds style from object, and returns a new TagBuilder 185 | */ 186 | ss(styles: StyleMap): TagBuilder; 187 | /** Adds style from object, and returns a new TagBuilder */ 188 | setStyles(styles: StyleMap): TagBuilder; 189 | /** 190 | * Shorthand for .removeStyles method. 191 | * Removes styles from this TagBuilder, and returns a new TagBuilder 192 | */ 193 | rs(...styleNames: string[]): TagBuilder; 194 | /** 195 | * Removes styles from this TagBuilder, and returns a new TagBuilder 196 | */ 197 | rmStyle(...styleNames: string[]): TagBuilder; 198 | private getMetaForTag; 199 | private isSelfClosingTag; 200 | private isStorableTag; 201 | private validateTagName; 202 | private sanitizeTagName; 203 | } 204 | export type PickArgType = T extends 'style' ? StyleSet[] : ValidTagChild[]; 205 | type BuilderFunctions = { 206 | [key in ValidTagName]: ((...children: PickArgType) => Tag) & TagBuilder; 207 | } & { 208 | /** 209 | * Create a new TagBuilder with specified tagName 210 | * @example 211 | * ```ts 212 | * tag('uknown-tag'); 213 | * ``` 214 | */ 215 | tag: (tagName: TagName, ...children: PickArgType) => TagBuilder; 216 | }; 217 | export declare const builders: Partial; 218 | export {}; 219 | -------------------------------------------------------------------------------- /dist/types/tag.d.ts: -------------------------------------------------------------------------------- 1 | import { TagName } from './custom-types/tag-names'; 2 | import { AttrSet } from './attributes'; 3 | import { FindBy, TagMeta, ValidTagChild } from './custom-types/types'; 4 | /** 5 | * Represents an html tag 6 | */ 7 | export declare class Tag { 8 | tagName: TagName; 9 | children: ValidTagChild[]; 10 | attr: AttrSet; 11 | _meta: TagMeta; 12 | get className(): import("./class-name").ClassName; 13 | get tagId(): string; 14 | constructor(tagName: TagName, children: ValidTagChild[], attr: AttrSet, meta: TagMeta); 15 | /** Append children */ 16 | append(child: ValidTagChild): void; 17 | /** Find a child by tag name */ 18 | findByTagName(targetTagName: TagName): Tag | null; 19 | /** Find a child by custom test */ 20 | findOneBy(test: FindBy): Tag | null; 21 | } 22 | -------------------------------------------------------------------------------- /dist/types/util.d.ts: -------------------------------------------------------------------------------- 1 | /** Receives a function, and returns just the body of the function as a string */ 2 | export declare function justFnBody(fn: Function): string; 3 | export declare const replaceDoubleQuotes: (str: string) => string; 4 | export declare const generateId: () => string; 5 | export declare const camelToDash: (str: any) => any; 6 | export declare const dashToCamel: (str: any) => any; 7 | export declare function isObject(obj: any): boolean; 8 | -------------------------------------------------------------------------------- /dist/util.js: -------------------------------------------------------------------------------- 1 | /** Receives a function, and returns just the body of the function as a string */ 2 | export function justFnBody(fn) { 3 | let fnStr = fn.toString(); 4 | fnStr = fnStr.replace(/^(.*{)/, ''); 5 | fnStr = fnStr.replace(/}$/, ''); 6 | fnStr = fnStr.replace(/^\(.*\)\s?=>\s?{/, ''); 7 | return fnStr.trim(); 8 | } 9 | export const replaceDoubleQuotes = (str) => str.replace(/"/g, "'"); 10 | export const generateId = () => `_hb${s4() + s4()}`; 11 | export const camelToDash = (str) => str.replace(/([A-Z])/g, (val) => `-${val.toLowerCase()}`); 12 | export const dashToCamel = (str) => str.replace(/(\-[a-z])/g, (val) => val.toUpperCase().replace('-', '')); 13 | export function isObject(obj) { 14 | return typeof obj === 'object' && !(obj instanceof Array); 15 | } 16 | const s4 = () => (((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); 17 | //# sourceMappingURL=util.js.map -------------------------------------------------------------------------------- /dist/util.js.map: -------------------------------------------------------------------------------- 1 | {"version":3,"file":"util.js","sourceRoot":"","sources":["../src/util.ts"],"names":[],"mappings":"AAAA,iFAAiF;AACjF,MAAM,UAAU,UAAU,CAAC,EAAY;IACrC,IAAI,KAAK,GAAG,EAAE,CAAC,QAAQ,EAAE,CAAC;IAC1B,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,QAAQ,EAAE,EAAE,CAAC,CAAC;IACpC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,EAAE,CAAC,CAAC;IAChC,KAAK,GAAG,KAAK,CAAC,OAAO,CAAC,kBAAkB,EAAE,EAAE,CAAC,CAAC;IAC9C,OAAO,KAAK,CAAC,IAAI,EAAE,CAAC;AACtB,CAAC;AACD,MAAM,CAAC,MAAM,mBAAmB,GAAG,CAAC,GAAW,EAAC,EAAE,CAAA,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,GAAG,CAAC,CAAC;AACzE,MAAM,CAAC,MAAM,UAAU,GAAG,GAAG,EAAE,CAAC,MAAM,EAAE,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC;AACpD,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,UAAU,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAI,GAAG,CAAC,WAAW,EAAE,EAAE,CAAC,CAAC;AAC9F,MAAM,CAAC,MAAM,WAAW,GAAG,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,OAAO,CAAC,YAAY,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,GAAG,CAAC,WAAW,EAAE,CAAC,OAAO,CAAC,GAAG,EAAE,EAAE,CAAC,CAAC,CAAC;AAC3G,MAAM,UAAU,QAAQ,CAAC,GAAQ;IAC/B,OAAO,OAAO,GAAG,KAAK,QAAQ,IAAI,CAAC,CAAC,GAAG,YAAY,KAAK,CAAC,CAAC;AAC5D,CAAC;AACD,MAAM,EAAE,GAAG,GAAE,EAAE,CAAA,CAAC,CAAC,CAAC,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,QAAQ,CAAC,EAAE,CAAC,CAAC,SAAS,CAAC,CAAC,CAAC,CAAC"} -------------------------------------------------------------------------------- /docs/.nojekyll: -------------------------------------------------------------------------------- 1 | TypeDoc added this file to prevent GitHub Pages from using Jekyll. You can turn off this behavior by setting the `githubPages` option to false. -------------------------------------------------------------------------------- /docs/assets/highlight.css: -------------------------------------------------------------------------------- 1 | :root { 2 | --light-hl-0: #001080; 3 | --dark-hl-0: #9CDCFE; 4 | --light-hl-1: #000000; 5 | --dark-hl-1: #D4D4D4; 6 | --light-hl-2: #000000; 7 | --dark-hl-2: #C8C8C8; 8 | --light-hl-3: #008000; 9 | --dark-hl-3: #6A9955; 10 | --light-hl-4: #AF00DB; 11 | --dark-hl-4: #C586C0; 12 | --light-hl-5: #A31515; 13 | --dark-hl-5: #CE9178; 14 | --light-hl-6: #0000FF; 15 | --dark-hl-6: #569CD6; 16 | --light-hl-7: #0070C1; 17 | --dark-hl-7: #4FC1FF; 18 | --light-hl-8: #795E26; 19 | --dark-hl-8: #DCDCAA; 20 | --light-hl-9: #800000; 21 | --dark-hl-9: #808080; 22 | --light-hl-10: #800000; 23 | --dark-hl-10: #569CD6; 24 | --light-hl-11: #000000FF; 25 | --dark-hl-11: #D4D4D4; 26 | --light-hl-12: #800000; 27 | --dark-hl-12: #D7BA7D; 28 | --light-hl-13: #E50000; 29 | --dark-hl-13: #9CDCFE; 30 | --light-hl-14: #0451A5; 31 | --dark-hl-14: #CE9178; 32 | --light-hl-15: #0000FF; 33 | --dark-hl-15: #CE9178; 34 | --light-code-background: #FFFFFF; 35 | --dark-code-background: #1E1E1E; 36 | } 37 | 38 | @media (prefers-color-scheme: light) { :root { 39 | --hl-0: var(--light-hl-0); 40 | --hl-1: var(--light-hl-1); 41 | --hl-2: var(--light-hl-2); 42 | --hl-3: var(--light-hl-3); 43 | --hl-4: var(--light-hl-4); 44 | --hl-5: var(--light-hl-5); 45 | --hl-6: var(--light-hl-6); 46 | --hl-7: var(--light-hl-7); 47 | --hl-8: var(--light-hl-8); 48 | --hl-9: var(--light-hl-9); 49 | --hl-10: var(--light-hl-10); 50 | --hl-11: var(--light-hl-11); 51 | --hl-12: var(--light-hl-12); 52 | --hl-13: var(--light-hl-13); 53 | --hl-14: var(--light-hl-14); 54 | --hl-15: var(--light-hl-15); 55 | --code-background: var(--light-code-background); 56 | } } 57 | 58 | @media (prefers-color-scheme: dark) { :root { 59 | --hl-0: var(--dark-hl-0); 60 | --hl-1: var(--dark-hl-1); 61 | --hl-2: var(--dark-hl-2); 62 | --hl-3: var(--dark-hl-3); 63 | --hl-4: var(--dark-hl-4); 64 | --hl-5: var(--dark-hl-5); 65 | --hl-6: var(--dark-hl-6); 66 | --hl-7: var(--dark-hl-7); 67 | --hl-8: var(--dark-hl-8); 68 | --hl-9: var(--dark-hl-9); 69 | --hl-10: var(--dark-hl-10); 70 | --hl-11: var(--dark-hl-11); 71 | --hl-12: var(--dark-hl-12); 72 | --hl-13: var(--dark-hl-13); 73 | --hl-14: var(--dark-hl-14); 74 | --hl-15: var(--dark-hl-15); 75 | --code-background: var(--dark-code-background); 76 | } } 77 | 78 | :root[data-theme='light'] { 79 | --hl-0: var(--light-hl-0); 80 | --hl-1: var(--light-hl-1); 81 | --hl-2: var(--light-hl-2); 82 | --hl-3: var(--light-hl-3); 83 | --hl-4: var(--light-hl-4); 84 | --hl-5: var(--light-hl-5); 85 | --hl-6: var(--light-hl-6); 86 | --hl-7: var(--light-hl-7); 87 | --hl-8: var(--light-hl-8); 88 | --hl-9: var(--light-hl-9); 89 | --hl-10: var(--light-hl-10); 90 | --hl-11: var(--light-hl-11); 91 | --hl-12: var(--light-hl-12); 92 | --hl-13: var(--light-hl-13); 93 | --hl-14: var(--light-hl-14); 94 | --hl-15: var(--light-hl-15); 95 | --code-background: var(--light-code-background); 96 | } 97 | 98 | :root[data-theme='dark'] { 99 | --hl-0: var(--dark-hl-0); 100 | --hl-1: var(--dark-hl-1); 101 | --hl-2: var(--dark-hl-2); 102 | --hl-3: var(--dark-hl-3); 103 | --hl-4: var(--dark-hl-4); 104 | --hl-5: var(--dark-hl-5); 105 | --hl-6: var(--dark-hl-6); 106 | --hl-7: var(--dark-hl-7); 107 | --hl-8: var(--dark-hl-8); 108 | --hl-9: var(--dark-hl-9); 109 | --hl-10: var(--dark-hl-10); 110 | --hl-11: var(--dark-hl-11); 111 | --hl-12: var(--dark-hl-12); 112 | --hl-13: var(--dark-hl-13); 113 | --hl-14: var(--dark-hl-14); 114 | --hl-15: var(--dark-hl-15); 115 | --code-background: var(--dark-code-background); 116 | } 117 | 118 | .hl-0 { color: var(--hl-0); } 119 | .hl-1 { color: var(--hl-1); } 120 | .hl-2 { color: var(--hl-2); } 121 | .hl-3 { color: var(--hl-3); } 122 | .hl-4 { color: var(--hl-4); } 123 | .hl-5 { color: var(--hl-5); } 124 | .hl-6 { color: var(--hl-6); } 125 | .hl-7 { color: var(--hl-7); } 126 | .hl-8 { color: var(--hl-8); } 127 | .hl-9 { color: var(--hl-9); } 128 | .hl-10 { color: var(--hl-10); } 129 | .hl-11 { color: var(--hl-11); } 130 | .hl-12 { color: var(--hl-12); } 131 | .hl-13 { color: var(--hl-13); } 132 | .hl-14 { color: var(--hl-14); } 133 | .hl-15 { color: var(--hl-15); } 134 | pre, code { background: var(--code-background); } 135 | -------------------------------------------------------------------------------- /docs/assets/navigation.js: -------------------------------------------------------------------------------- 1 | window.navigationData = "data:application/octet-stream;base64,H4sIAAAAAAAAE5WVS3OCMBSF/0u61dZXH7rzMZ3ppo+RneM4IaQSjYlDkk6djv+9AYQEISndQc65Xw7hXlj9AIm/JZgAKGVCQiWxAB1whDLWawceKYrFndFuY3mg2rAnLAKTQQegmNAowQxMViVqqu1LLA0HUShElXPxVHn9wdN5fe6UoKyuy+AB1zNl2ibVWmWap/bXCqlIZZFKlz9XzENeT5SutsoSwK2n+iaXc8KoPx4NeyNrb63OlGbi5A+GcblQG8RZdlmCvmBCYFiiCkP1qYYDixHmuwgnozB4GLotIIoN4VMxJAlnF0IuV+sf7OeIsLc+l331HLmLOfJUbjHDCZTYWV4Yagy7nYQ80YYOz5ZbNdQydTZOXM4odH9Xy6a21Iv/7+li97Q4uAY0bNsNXR2txc1FbBujNh1WmpJlfPVwJto7Qftpsg1OR+v1SH1XhVm2q5C98WP/vu242Ejn1NgnpySh9SNLV1udFdLfORrwBRSNs5NxLI9vgLQc8HnqdZIsT4uBeomcIGPxcIh4C3cYSSelMHgYOyXkM5vx6OSkGIuHk+AjhQgvuNKv+UPxyi/2Ctjgbfh0rH8Byzk2xrcHAAA=" -------------------------------------------------------------------------------- /docs/assets/search.js: -------------------------------------------------------------------------------- 1 | window.searchData = "data:application/octet-stream;base64,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"; -------------------------------------------------------------------------------- /docs/variables/hobo._context.html: -------------------------------------------------------------------------------- 1 | _context | hobo-js
2 |
3 | 10 |
11 |
12 |
13 |
14 | 18 |

Variable _context

19 |
_context: HoboContext = ...
22 |
23 | 37 |
47 |
48 |

Generated using TypeDoc

49 |
-------------------------------------------------------------------------------- /examples/_test.ts: -------------------------------------------------------------------------------- 1 | import { attach, doc, generate } from '../src/hobo'; 2 | import { builders } from '../src/tag-builder'; 3 | 4 | const { div, img } = builders; 5 | 6 | console.log(generate(img!.id('src').b())) 7 | console.log(generate(img!.as('color', 'red').b())) 8 | -------------------------------------------------------------------------------- /examples/generated/readme-1.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | My Page Title 6 | 17 | 18 | 19 |
20 |

I'm a child of div.wrapper

21 | And so am I 22 |
23 | Click me 24 |
25 | 28 | 29 | 30 | -------------------------------------------------------------------------------- /examples/generated/tag-attributes.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/generated/tag-classes.html: -------------------------------------------------------------------------------- 1 |
2 |
3 |
4 | -------------------------------------------------------------------------------- /examples/generated/tag-inline-styles.html: -------------------------------------------------------------------------------- 1 | 2 | -------------------------------------------------------------------------------- /examples/generated/tag-scripts.html: -------------------------------------------------------------------------------- 1 |
2 | 5 |
6 | -------------------------------------------------------------------------------- /examples/generated/tag-styles.html: -------------------------------------------------------------------------------- 1 |
2 | 10 |
11 | -------------------------------------------------------------------------------- /examples/hobo-in-js.js: -------------------------------------------------------------------------------- 1 | const { builders } = require('../dist/hobo.js'); 2 | 3 | console.log(builders.div()); -------------------------------------------------------------------------------- /examples/readme-1.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import * as fs from 'fs'; 3 | import { builders, doc, generate } from '../src/hobo'; 4 | 5 | const { div, p, span, b, script, button, style, a, hr } = builders; 6 | 7 | // Create a document. It will create the html, head and body tags. A title will also be added to the head. 8 | // NOTE that doc, automatically attaches the body (read more on attaching below) 9 | const myPage = doc('My Page Title'); 10 | 11 | // This will create a style tag with the given css definitions inside 12 | myPage.head.append( 13 | style({ 14 | '.wrapper': { 15 | background: 'black', 16 | display: 'flex', 17 | alignItems: 'center', 18 | justifyContent: 'center', 19 | ':hover': { 20 | background: 'red' 21 | } 22 | }, 23 | }), 24 | ); 25 | 26 | // Creates a `div` with class wrapper, and 3 children (p, b, button). 27 | // `.a` indicates hobo to attach the `div` to the currently attached tag 28 | // you can attach manually to any tag, but by calling `doc`, the `body` will be attached 29 | div.a.addClass('wrapper').build( 30 | p("I'm a child of div.wrapper"), 31 | b.addStyle('color', 'aliceblue')('And so am I'), 32 | hr, 33 | a.addAttr('href', 'http://example.com').b('Click me'), 34 | button.id('button-id').b("I'm also a child"), 35 | ); 36 | 37 | // The code inside the function will be inserted into a script tag 38 | script.a(() => { 39 | const btn = document.querySelector('#button-id'); 40 | }); 41 | 42 | fs.writeFileSync(path.join(__dirname, 'generated/readme-1.html'), generate(myPage.doc)); 43 | -------------------------------------------------------------------------------- /examples/tag-attributes.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import * as fs from 'fs'; 3 | import { builders, generate } from '../src/hobo'; 4 | 5 | const { a } = builders; 6 | 7 | // Add single attribute 8 | const root = a 9 | .aa('href', 'https://example.com') 10 | // Add class names directly 11 | .am({ href: 'https://example.com', tooltip: 'Go to website' }); 12 | 13 | fs.writeFileSync(path.join(__dirname, 'generated/tag-attributes.html'), generate(root.b())); 14 | -------------------------------------------------------------------------------- /examples/tag-classes.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import * as fs from 'fs'; 3 | import { builders, doc, generate } from '../src/hobo'; 4 | 5 | const { div } = builders; 6 | 7 | // Add class names using builder helper methods 8 | const root = div.ac('card-wrapper').append(div.ac('card', 'centered')); 9 | 10 | // Add class names directly 11 | root.attr.className.add('card-wrapper'); 12 | 13 | fs.writeFileSync(path.join(__dirname, 'generated/tag-classes.html'), generate(root.b())); -------------------------------------------------------------------------------- /examples/tag-inline-styles.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import * as fs from 'fs'; 3 | import { builders, generate } from '../src/hobo'; 4 | 5 | const { a } = builders; 6 | 7 | // Add single style 8 | const root = a 9 | .as('color', 'bisque') 10 | // Add multiple styles at once 11 | .ss({ color: 'black', fontWeight: 'bold' }); 12 | 13 | fs.writeFileSync(path.join(__dirname, 'generated/tag-inline-styles.html'), generate(root.b())); 14 | -------------------------------------------------------------------------------- /examples/tag-script.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import * as fs from 'fs'; 3 | import { builders, generate } from '../src/hobo'; 4 | 5 | const { div, script } = builders; 6 | 7 | const root = div.append( 8 | script(() => { 9 | const rootDiv = document.querySelector('div'); 10 | }), 11 | ); 12 | 13 | fs.writeFileSync(path.join(__dirname, 'generated/tag-scripts.html'), generate(root.b())); 14 | -------------------------------------------------------------------------------- /examples/tag-style.ts: -------------------------------------------------------------------------------- 1 | import path from 'path'; 2 | import * as fs from 'fs'; 3 | import { builders, generate } from '../src/hobo'; 4 | 5 | const { div, style } = builders; 6 | 7 | const root = div.append( 8 | style( 9 | { 10 | body: { 11 | backgroundColor: 'red', 12 | }, 13 | }, 14 | { 15 | '.some-class': { 16 | color: 'blue', 17 | }, 18 | }, 19 | ), 20 | ); 21 | 22 | fs.writeFileSync(path.join(__dirname, 'generated/tag-styles.html'), generate(root.b())); 23 | -------------------------------------------------------------------------------- /hobo-header.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/nombrekeff/hobo-js/1cd54a3fe83bbd15820fb2e05fb6736a35efba6b/hobo-header.png -------------------------------------------------------------------------------- /jest.config.js: -------------------------------------------------------------------------------- 1 | // Sync object 2 | const config= { 3 | verbose: true, 4 | transform: { 5 | '^.+\\.tsx?$': 'ts-jest', 6 | }, 7 | }; 8 | module.exports= config; 9 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "hobo-js", 3 | "version": "1.0.2", 4 | "description": "A little utility to generate html inside your js/ts code", 5 | "main": "dist/hobo.js", 6 | "module": "dist/hobo.mjs", 7 | "unpkg": "dist/hobo.umd.min.js", 8 | "types": "dist/types/hobo.d.ts", 9 | "files": [ 10 | "dist" 11 | ], 12 | "scripts": { 13 | "test": "jest --coverage", 14 | "clean": "rm -fr dist", 15 | "build": "npm run clean && tsc -d --project tsconfig.build.json && build:docs && npm run bundle:esm && npm run bundle:esm:min && npm run bundle:umd && npm run bundle:umd:min", 16 | "bundle:esm": "rollup dist/hobo.js --file dist/hobo.mjs --format esm", 17 | "bundle:esm:min": "terser --ecma 6 --compress --mangle --module -o dist/hobo.min.mjs -- dist/hobo.mjs && gzip -9 -c dist/hobo.min.mjs > dist/hobo.min.mjs.gz", 18 | "bundle:umd": "rollup dist/hobo.js --file dist/hobo.umd.js --format umd --name sayHello", 19 | "bundle:umd:min": "terser --ecma 6 --compress --mangle -o dist/hobo.umd.min.js -- dist/hobo.umd.js && gzip -9 -c dist/hobo.umd.min.js > dist/hobo.umd.min.js.gz", 20 | "build:docs": "typedoc --out docs src/*.ts" 21 | }, 22 | "keywords": [ 23 | "js", 24 | "ts", 25 | "generate", 26 | "html", 27 | "from", 28 | "code", 29 | "typed", 30 | "template", 31 | "build", 32 | "in-code" 33 | ], 34 | "author": "nombrekeff", 35 | "repository": { 36 | "git": "https://github.com/nombrekeff/hobo-js" 37 | }, 38 | "license": "ISC", 39 | "devDependencies": { 40 | "@types/jest": "^29.5.5", 41 | "@types/node": "^20.7.0", 42 | "jest": "^29.7.0", 43 | "rollup": "^3.29.4", 44 | "terser": "^5.21.0", 45 | "ts-jest": "^29.1.1", 46 | "ts-loader": "^9.4.4", 47 | "typedoc": "^0.25.1", 48 | "typescript": "^5.2.2", 49 | "webpack": "^5.88.2", 50 | "webpack-cli": "^5.1.4" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /scripts/generate_api_docs.js: -------------------------------------------------------------------------------- 1 | const fs = require('fs'); 2 | const path = require('path'); 3 | const { exec } = require("child_process"); 4 | 5 | const jsFilesFoldPath = path.join(__dirname, '../dist'); 6 | const output = path.join(__dirname, '../docs'); 7 | const makeDocsCommand = (name, path) => `jsdoc2md -f ${path}/${name}.js > ${output}/${name}.api.md`; 8 | 9 | const ignoredFiles = [ 10 | 'types', 11 | 'generation', 12 | 'util.js', 13 | 'tag-names.js', 14 | 'css-property-values.js', 15 | 'css-properties.js', 16 | 'css-property-values.js', 17 | ]; 18 | 19 | function processFolder(folderPath) { 20 | const inFiles = fs.readdirSync(folderPath); 21 | 22 | for (const file of inFiles) { 23 | // Skip map files 24 | if (file.includes('map')) continue; 25 | if (ignoredFiles.includes(file)) continue; 26 | 27 | const filePath = path.join(folderPath, file); 28 | const fstat = fs.statSync(filePath); 29 | 30 | if (fstat.isDirectory()) { 31 | processFolder(filePath); 32 | } 33 | else { 34 | const extension = path.extname(filePath); 35 | const name = path.basename(filePath, extension).replace(extension, ''); 36 | 37 | const command = makeDocsCommand(name, folderPath); 38 | exec(command, (error, _, stderr) => { 39 | if (error) { 40 | console.log(`error: ${error.message}`); 41 | return; 42 | } 43 | if (stderr) { 44 | console.log(`stderr: ${stderr}`); 45 | return; 46 | } 47 | }); 48 | } 49 | } 50 | 51 | } 52 | 53 | fs.rmSync(output, {recursive: true}); 54 | fs.mkdirSync(output); 55 | 56 | processFolder(jsFilesFoldPath); 57 | 58 | 59 | -------------------------------------------------------------------------------- /src/attributes.ts: -------------------------------------------------------------------------------- 1 | import { ClassName } from './class-name'; 2 | import { StyleSet } from './style'; 3 | 4 | /** 5 | * Represents a tag's attribute set. 6 | */ 7 | export class AttrSet { 8 | className: ClassName = new ClassName(); 9 | id?: string; 10 | style: StyleSet = new StyleSet(); 11 | 12 | additionalAttributes: { [key: string]: string } = {}; 13 | 14 | copy() { 15 | const newSet = new AttrSet(); 16 | newSet.className = this.className.copy(); 17 | newSet.id = this.id; 18 | newSet.style = this.style.copy(); 19 | newSet.additionalAttributes = { ...this.additionalAttributes }; 20 | return newSet; 21 | } 22 | 23 | /** Set single attribute */ 24 | set(key: string, value: string): AttrSet { 25 | this.additionalAttributes[key] = value; 26 | return this; 27 | } 28 | 29 | /** Remove attributes */ 30 | remove(...attrs: string[]): AttrSet { 31 | for (const cn of attrs) { 32 | delete this.additionalAttributes[cn]; 33 | } 34 | return this; 35 | } 36 | 37 | /** Check if an attribute is set */ 38 | has(key: string): boolean { 39 | return key in this.additionalAttributes; 40 | } 41 | } 42 | -------------------------------------------------------------------------------- /src/class-name.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * Represents a tag's class list. 3 | */ 4 | export class ClassName { 5 | classNames: string[] = []; 6 | 7 | constructor(classNames: string[] = []) { 8 | this.classNames = classNames; 9 | } 10 | 11 | copy() { 12 | const newClassName = new ClassName(); 13 | newClassName.classNames = [...this.classNames]; 14 | return newClassName; 15 | } 16 | 17 | /** 18 | * Returns a string of all the class names separated by spaces. 19 | */ 20 | raw(): string { 21 | return this.classNames.join(' '); 22 | } 23 | 24 | /** Add one or more class names */ 25 | add(...classNames: string[]): ClassName { 26 | for (const cn of classNames) { 27 | if (!this.has(cn)) { 28 | this.classNames.push(cn); 29 | } 30 | } 31 | 32 | return this; 33 | } 34 | 35 | /** Remove one or more class names */ 36 | remove(...classNames: string[]): ClassName { 37 | for (const cn of classNames) { 38 | if (!this.has(cn)) return this; 39 | 40 | const index = this.classNames.indexOf(cn); 41 | this.classNames.splice(index, 1); 42 | } 43 | 44 | return this; 45 | } 46 | 47 | /** Check if a class name is present. */ 48 | has(str: string): boolean { 49 | return this.classNames.includes(str); 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /src/custom-types/colors.ts: -------------------------------------------------------------------------------- 1 | export type NamedColor = 'black'| 2 | 'silver'| 3 | 'gray'| 4 | 'white'| 5 | 'maroon'| 6 | 'red'| 7 | 'purple'| 8 | 'fuchsia'| 9 | 'green'| 10 | 'lime'| 11 | 'olive'| 12 | 'yellow'| 13 | 'navy'| 14 | 'blue'| 15 | 'teal'| 16 | 'aqua'| 17 | 'aliceblue'| 18 | 'antiquewhite'| 19 | 'aqua'| 20 | 'aquamarine'| 21 | 'azure'| 22 | 'beige'| 23 | 'bisque'| 24 | 'black'| 25 | 'blanchedalmond'| 26 | 'blue'| 27 | 'blueviolet'| 28 | 'brown'| 29 | 'burlywood'| 30 | 'cadetblue'| 31 | 'chartreuse'| 32 | 'chocolate'| 33 | 'coral'| 34 | 'cornflowerblue'| 35 | 'cornsilk'| 36 | 'crimson'| 37 | 'cyan'| 38 | 'darkblue'| 39 | 'darkcyan'| 40 | 'darkgoldenrod'| 41 | 'darkgray'| 42 | 'darkgreen'| 43 | 'darkgrey'| 44 | 'darkkhaki'| 45 | 'darkmagenta'| 46 | 'darkolivegreen'| 47 | 'darkorange'| 48 | 'darkorchid'| 49 | 'darkred'| 50 | 'darksalmon'| 51 | 'darkseagreen'| 52 | 'darkslateblue'| 53 | 'darkslategray'| 54 | 'darkslategrey'| 55 | 'darkturquoise'| 56 | 'darkviolet'| 57 | 'deeppink'| 58 | 'deepskyblue'| 59 | 'dimgray'| 60 | 'dimgrey'| 61 | 'dodgerblue'| 62 | 'firebrick'| 63 | 'floralwhite'| 64 | 'forestgreen'| 65 | 'fuchsia'| 66 | 'gainsboro'| 67 | 'ghostwhite'| 68 | 'gold'| 69 | 'goldenrod'| 70 | 'gray'| 71 | 'green'| 72 | 'greenyellow'| 73 | 'grey'| 74 | 'honeydew'| 75 | 'hotpink'| 76 | 'indianred'| 77 | 'indigo'| 78 | 'ivory'| 79 | 'khaki'| 80 | 'lavender'| 81 | 'lavenderblush'| 82 | 'lawngreen'| 83 | 'lemonchiffon'| 84 | 'lightblue'| 85 | 'lightcoral'| 86 | 'lightcyan'| 87 | 'lightgoldenrodyellow'| 88 | 'lightgray'| 89 | 'lightgreen'| 90 | 'lightgrey'| 91 | 'lightpink'| 92 | 'lightsalmon'| 93 | 'lightseagreen'| 94 | 'lightskyblue'| 95 | 'lightslategray'| 96 | 'lightslategrey'| 97 | 'lightsteelblue'| 98 | 'lightyellow'| 99 | 'lime'| 100 | 'limegreen'| 101 | 'linen'| 102 | 'magenta'| 103 | 'maroon'| 104 | 'mediumaquamarine'| 105 | 'mediumblue'| 106 | 'mediumorchid'| 107 | 'mediumpurple'| 108 | 'mediumseagreen'| 109 | 'mediumslateblue'| 110 | 'mediumspringgreen'| 111 | 'mediumturquoise'| 112 | 'mediumvioletred'| 113 | 'midnightblue'| 114 | 'mintcream'| 115 | 'mistyrose'| 116 | 'moccasin'| 117 | 'navajowhite'| 118 | 'navy'| 119 | 'oldlace'| 120 | 'olive'| 121 | 'olivedrab'| 122 | 'orange'| 123 | 'orangered'| 124 | 'orchid'| 125 | 'palegoldenrod'| 126 | 'palegreen'| 127 | 'paleturquoise'| 128 | 'palevioletred'| 129 | 'papayawhip'| 130 | 'peachpuff'| 131 | 'peru'| 132 | 'pink'| 133 | 'plum'| 134 | 'powderblue'| 135 | 'purple'| 136 | 'rebeccapurple'| 137 | 'red'| 138 | 'rosybrown'| 139 | 'royalblue'| 140 | 'saddlebrown'| 141 | 'salmon'| 142 | 'sandybrown'| 143 | 'seagreen'| 144 | 'seashell'| 145 | 'sienna'| 146 | 'silver'| 147 | 'skyblue'| 148 | 'slateblue'| 149 | 'slategray'| 150 | 'slategrey'| 151 | 'snow'| 152 | 'springgreen'| 153 | 'steelblue'| 154 | 'tan'| 155 | 'teal'| 156 | 'thistle'| 157 | 'tomato'| 158 | 'turquoise'| 159 | 'violet'| 160 | 'wheat'| 161 | 'white'| 162 | 'whitesmoke'| 163 | 'yellow'| 164 | 'yellowgreen'; -------------------------------------------------------------------------------- /src/custom-types/css-properties.ts: -------------------------------------------------------------------------------- 1 | export type CssProperty = 2 | | 'color' 3 | | 'border' 4 | | 'margin' 5 | | 'fontStyle' 6 | | 'transform' 7 | | 'backgroundColor' 8 | | 'alignContent' 9 | | 'alignItems' 10 | | 'alignSelf' 11 | | 'all' 12 | | 'animation' 13 | | 'animationDelay' 14 | | 'animationDirection' 15 | | 'animationDuration' 16 | | 'animationFillMode' 17 | | 'animationIterationCount' 18 | | 'animationName' 19 | | 'animationPlayState' 20 | | 'animationTimingFunction' 21 | | 'backfaceVisibility' 22 | | 'background' 23 | | 'backgroundAttachment' 24 | | 'backgroundBlendMode' 25 | | 'backgroundClip' 26 | | 'backgroundColor' 27 | | 'backgroundImage' 28 | | 'backgroundOrigin' 29 | | 'backgroundPosition' 30 | | 'backgroundRepeat' 31 | | 'backgroundSize' 32 | | 'border' 33 | | 'borderBottom' 34 | | 'borderBottomColor' 35 | | 'borderBottomLeftRadius' 36 | | 'borderBottomRightRadius' 37 | | 'borderBottomStyle' 38 | | 'borderBottomWidth' 39 | | 'borderCollapse' 40 | | 'borderColor' 41 | | 'borderImage' 42 | | 'borderImageOutset' 43 | | 'borderImageRepeat' 44 | | 'borderImageSlice' 45 | | 'borderImageSource' 46 | | 'borderImageWidth' 47 | | 'borderLeft' 48 | | 'borderLeftColor' 49 | | 'borderLeftStyle' 50 | | 'borderLeftWidth' 51 | | 'borderRadius' 52 | | 'borderRight' 53 | | 'borderRightColor' 54 | | 'borderRightStyle' 55 | | 'borderRightWidth' 56 | | 'borderSpacing' 57 | | 'borderStyle' 58 | | 'borderTop' 59 | | 'borderTopColor' 60 | | 'borderTopLeftRadius' 61 | | 'borderTopRightRadius' 62 | | 'borderTopStyle' 63 | | 'borderTopWidth' 64 | | 'borderWidth' 65 | | 'bottom' 66 | | 'boxShadow' 67 | | 'boxSizing' 68 | | 'captionSide' 69 | | 'caretColor' 70 | | '@charset' 71 | | 'clear' 72 | | 'clip' 73 | | 'clipPath' 74 | | 'color' 75 | | 'columnCount' 76 | | 'columnFill' 77 | | 'columnGap' 78 | | 'columnRule' 79 | | 'columnRuleColor' 80 | | 'columnRuleStyle' 81 | | 'columnRuleWidth' 82 | | 'columnSpan' 83 | | 'columnWidth' 84 | | 'columns' 85 | | 'content' 86 | | 'counterIncrement' 87 | | 'counterReset' 88 | | 'cursor' 89 | | 'direction' 90 | | 'display' 91 | | 'emptyCells' 92 | | 'filter' 93 | | 'flex' 94 | | 'flexBasis' 95 | | 'flexDirection' 96 | | 'flexFlow' 97 | | 'flexGrow' 98 | | 'flexShrink' 99 | | 'flexWrap' 100 | | 'float' 101 | | 'font' 102 | | '@fontFace' 103 | | 'fontFamily' 104 | | 'fontKerning' 105 | | 'fontSize' 106 | | 'fontSizeAdjust' 107 | | 'fontStretch' 108 | | 'fontStyle' 109 | | 'fontVariant' 110 | | 'fontWeight' 111 | | 'grid' 112 | | 'gridArea' 113 | | 'gridAutoColumns' 114 | | 'gridAutoFlow' 115 | | 'gridAutoRows' 116 | | 'gridColumn' 117 | | 'gridColumnEnd' 118 | | 'gridColumnGap' 119 | | 'gridColumnStart' 120 | | 'gridGap' 121 | | 'gridRow' 122 | | 'gridRowEnd' 123 | | 'gridRowGap' 124 | | 'gridRowStart' 125 | | 'gridTemplate' 126 | | 'gridTemplateAreas' 127 | | 'gridTemplateColumns' 128 | | 'gridTemplateRows' 129 | | 'height' 130 | | 'hyphens' 131 | | '@import' 132 | | 'justifyContent' 133 | | '@keyframes' 134 | | 'left' 135 | | 'letterSpacing' 136 | | 'lineHeight' 137 | | 'listStyle' 138 | | 'listStyleImage' 139 | | 'listStylePosition' 140 | | 'listStyleType' 141 | | 'margin' 142 | | 'marginBottom' 143 | | 'marginLeft' 144 | | 'marginRight' 145 | | 'marginTop' 146 | | 'maxHeight' 147 | | 'maxWidth' 148 | | '@media' 149 | | 'minHeight' 150 | | 'minWidth' 151 | | 'objectFit' 152 | | 'objectPosition' 153 | | 'opacity' 154 | | 'order' 155 | | 'outline' 156 | | 'outlineColor' 157 | | 'outlineOffset' 158 | | 'outlineStyle' 159 | | 'outlineWidth' 160 | | 'overflow' 161 | | 'overflowX' 162 | | 'overflowY' 163 | | 'padding' 164 | | 'paddingBottom' 165 | | 'paddingLeft' 166 | | 'paddingRight' 167 | | 'paddingTop' 168 | | 'pageBreakAfter' 169 | | 'pageBreakBefore' 170 | | 'pageBreakInside' 171 | | 'perspective' 172 | | 'perspectiveOrigin' 173 | | 'pointerEvents' 174 | | 'position' 175 | | 'quotes' 176 | | 'right' 177 | | 'scrollBehavior' 178 | | 'tableLayout' 179 | | 'textAlign' 180 | | 'textAlignLast' 181 | | 'textDecoration' 182 | | 'textDecorationColor' 183 | | 'textDecorationLine' 184 | | 'textDecorationStyle' 185 | | 'textIndent' 186 | | 'textJustify' 187 | | 'textOverflow' 188 | | 'textShadow' 189 | | 'textTransform' 190 | | 'top' 191 | | 'transform' 192 | | 'transformOrigin' 193 | | 'transformStyle' 194 | | 'transition' 195 | | 'transitionDelay' 196 | | 'transitionDuration' 197 | | 'transitionProperty' 198 | | 'transitionTimingFunction' 199 | | 'userSelect' 200 | | 'verticalAlign' 201 | | 'visibility' 202 | | 'whiteSpace' 203 | | 'width' 204 | | 'wordBreak' 205 | | 'wordSpacing' 206 | | 'wordWrap' 207 | | 'writingMode' 208 | | 'zIndex' 209 | | (string & {}); 210 | -------------------------------------------------------------------------------- /src/custom-types/css-property-values.ts: -------------------------------------------------------------------------------- 1 | import { NamedColor } from './colors'; 2 | 3 | // Create script to collect all the values, wherever posible. 4 | // Scrap https://dofactory.com/css/properties#list or other site, and get all single word options 5 | 6 | export type PickPropertyValues = T extends 'color' 7 | ? ColorOptions 8 | : T extends 'alignContent' 9 | ? AlignContentOptions 10 | : T extends 'alignItems' 11 | ? AlignItemsOptions 12 | : T extends 'alignSelf' 13 | ? AlignSelfOptions 14 | : T extends 'all' 15 | ? AllOptions 16 | : T extends 'accentColor' 17 | ? ColorOptions 18 | : T extends 'animationDirection' 19 | ? AnimationDirectionOptions 20 | : T extends 'animationFillMode' 21 | ? AnimationFillModeOptions 22 | : T extends 'animationPlayState' 23 | ? AnimationPlayStateOptions 24 | : T extends 'animationPlayState' 25 | ? AnimationPlayStateOptions 26 | : T extends 'borderStyle' 27 | ? BorderStyleOptions 28 | : T extends 'background' 29 | ? ColorOptions 30 | : T extends 'backgroundColor' 31 | ? ColorOptions 32 | : T extends 'backgroundImage' 33 | ? BackgroundImageOptions 34 | : T extends 'backgroundRepeat' 35 | ? BackgroundRepeatOptions 36 | : T extends 'backgroundAttachment' 37 | ? BackgroundAttachmentOptions 38 | : T extends 'backgroundPosition' 39 | ? BackgroundPositionOptions 40 | : T extends 'position' 41 | ? PPositionOptions 42 | : T extends 'transform' 43 | ? TransformOptions 44 | : T extends 'fontStyle' 45 | ? FontStyleOptions 46 | : T extends 'fontWeight' 47 | ? FontWeightOptions 48 | : T extends 'flexDirection' 49 | ? FlexDirectionOptions 50 | : T extends 'zIndex' 51 | ? Number 52 | : T extends 'top' 53 | ? Number 54 | : T extends 'display' 55 | ? DisplayOptions 56 | : T extends 'bottom' 57 | ? Number 58 | : T extends 'left' 59 | ? Number 60 | : T extends 'right' 61 | ? Number 62 | : string & {}; 63 | 64 | export type CommonOptions = 'initial' | 'inherit' | (string & {}); 65 | export type ColorOptions = NamedColor | (string & {}); 66 | export type AlignContentOptions = 67 | | 'flex-wrap' 68 | | 'stretch' 69 | | 'center' 70 | | 'flex-start' 71 | | 'flex-end' 72 | | 'space-between' 73 | | 'space-around' 74 | | CommonOptions; 75 | export type AlignItemsOptions = 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'baseline' | CommonOptions; 76 | export type DisplayOptions = 77 | | 'inline' 78 | | 'block' 79 | | 'contents' 80 | | 'flex' 81 | | 'grid' 82 | | 'inline-block' 83 | | 'inline-flex' 84 | | 'inline-grid' 85 | | 'inline-table' 86 | | 'list-item' 87 | | 'run-in' 88 | | 'table' 89 | | 'table-caption' 90 | | 'table-column-group' 91 | | 'table-header-group' 92 | | 'table-footer-group' 93 | | 'table-row-group' 94 | | 'table-cell' 95 | | 'table-column' 96 | | 'table-row' 97 | | 'none' 98 | | CommonOptions; 99 | type AlignSelfOptions = 'auto' | 'stretch' | 'center' | 'flex-start' | 'flex-end' | 'baseline' | CommonOptions; 100 | type AllOptions = CommonOptions | 'unset'; 101 | type AnimationDirectionOptions = 102 | | 'normal' 103 | | 'reverse' 104 | | 'alternate' 105 | | 'alternate-reverse' 106 | | 'initial' 107 | | 'inherit' 108 | | (string & {}); 109 | type AnimationFillModeOptions = 'none' | 'forwards' | 'backwards' | 'both' | CommonOptions; 110 | type AnimationPlayStateOptions = 'paused' | 'running' | CommonOptions; 111 | type BorderStyleOptions = 112 | | 'none' 113 | | 'no' 114 | | 'none' 115 | | 'hidden' 116 | | 'dotted' 117 | | 'dashed' 118 | | 'solid' 119 | | 'double' 120 | | 'groove' 121 | | 'ridge' 122 | | 'inset' 123 | | 'outset' 124 | | CommonOptions; 125 | export type BackgroundImageOptions = 126 | | 'url()' 127 | | 'none' 128 | | 'conic-gradient()' 129 | | 'linear-gradient()' 130 | | 'radial-gradient()' 131 | | 'repeating-conic-gradient()' 132 | | 'repeating-linear-gradient()' 133 | | 'repeating-radial-gradient()' 134 | | 'initial' 135 | | 'inherit'; 136 | type BackgroundRepeatOptions = 137 | | 'repeat' 138 | | 'no' 139 | | 'repeat' 140 | | 'repeat-x' 141 | | 'repeat-y' 142 | | 'no-repeat' 143 | | 'space' 144 | | 'round' 145 | | CommonOptions; 146 | type BackgroundAttachmentOptions = 'scroll' | 'no' | 'scroll' | 'fixed' | 'local' | CommonOptions; 147 | type BackgroundPositionOptions = 148 | | 'left top' 149 | | 'left center' 150 | | 'left bottom' 151 | | 'right top' 152 | | 'right center' 153 | | 'right bottom' 154 | | 'center top' 155 | | 'center center' 156 | | 'center bottom' 157 | | 'inherit' 158 | | 'initial' 159 | | (string & {}); 160 | export type PPositionOptions = 'static' | 'fixed' | 'absolute' | 'relative' | 'sticky' | CommonOptions; 161 | export type TransformOptions = 162 | | 'none' 163 | | 'matrix(n,n,n,n,n,n)' 164 | | 'matrix3d(n,n,n,n,n,n,n,n,n,n,n,n,n,n,n,n)' 165 | | 'translate(x,y)' 166 | | 'translate3d(x,y,z)' 167 | | 'translateX(x)' 168 | | 'translateY(y)' 169 | | 'translateZ(z)' 170 | | 'scale(x,y)' 171 | | 'scale3d(x,y,z)' 172 | | 'scaleX(x)' 173 | | 'scaleY(y)' 174 | | 'scaleZ(z)' 175 | | 'rotate(angle)' 176 | | 'rotate3d(x,y,z,angle)' 177 | | 'rotateX(angle)' 178 | | 'rotateY(angle)' 179 | | 'rotateZ(angle)' 180 | | 'skew(x-angle,y-angle)' 181 | | 'skewX(angle)' 182 | | 'skewY(angle)' 183 | | 'perspective(n)' 184 | | CommonOptions; 185 | export type FontWeightOptions = 186 | | 'normal' 187 | | 'bold' 188 | | 'bolder' 189 | | 'lighter' 190 | | '100' 191 | | '200' 192 | | '300' 193 | | '400' 194 | | '500' 195 | | '600' 196 | | '700' 197 | | '800' 198 | | '900' 199 | | CommonOptions; 200 | export type FontStyleOptions = 'normal' | 'italic' | 'oblique' | CommonOptions; 201 | export type FlexDirectionOptions = 'row' | 'column' | 'row-reverse' | 'column-reverse' | CommonOptions; 202 | -------------------------------------------------------------------------------- /src/custom-types/tag-names.ts: -------------------------------------------------------------------------------- 1 | /** 2 | * @export 3 | * List of all known self-closing HTML tags 4 | */ 5 | export const selfClosingTags = [ 6 | 'area', 7 | 'base', 8 | 'br', 9 | 'col', 10 | 'embed', 11 | 'hr', 12 | 'img', 13 | 'input', 14 | 'link', 15 | 'meta', 16 | 'param', 17 | 'source', 18 | 'track', 19 | 'wbr', 20 | ]; 21 | 22 | /** List of all known closing HTML tags */ 23 | export const closingTags = [ 24 | 'a', 25 | 'abbr', 26 | 'acronym', 27 | 'address', 28 | 'article', 29 | 'aside', 30 | 'audio', 31 | 'b', 32 | 'basefont', 33 | 'bdi', 34 | 'bdo', 35 | 'big', 36 | 'blockquote', 37 | 'body', 38 | 'button', 39 | 'canvas', 40 | 'caption', 41 | 'center', 42 | 'cite', 43 | 'code', 44 | 'colgroup', 45 | 'data', 46 | 'datalist', 47 | 'dd', 48 | 'del', 49 | 'details', 50 | 'dfn', 51 | 'dialog', 52 | 'div', 53 | 'dl', 54 | 'dt', 55 | 'em', 56 | 'fieldset', 57 | 'figcaption', 58 | 'figure', 59 | 'footer', 60 | 'form', 61 | 'h1', 62 | 'h2', 63 | 'h3', 64 | 'h4', 65 | 'h5', 66 | 'h6', 67 | 'head', 68 | 'header', 69 | 'html', 70 | 'i', 71 | 'iframe', 72 | 'ins', 73 | 'kbd', 74 | 'label', 75 | 'legend', 76 | 'li', 77 | 'main', 78 | 'map', 79 | 'mark', 80 | 'meter', 81 | 'nav', 82 | 'noscript', 83 | 'object', 84 | 'ol', 85 | 'optgroup', 86 | 'option', 87 | 'output', 88 | 'p', 89 | 'picture', 90 | 'pre', 91 | 'progress', 92 | 'q', 93 | 'rp', 94 | 'rt', 95 | 'ruby', 96 | 's', 97 | 'samp', 98 | 'script', 99 | 'section', 100 | 'select', 101 | 'small', 102 | 'span', 103 | 'strong', 104 | 'style', 105 | 'sub', 106 | 'summary', 107 | 'sup', 108 | 'svg', 109 | 'table', 110 | 'tbody', 111 | 'td', 112 | 'template', 113 | 'textarea', 114 | 'tfoot', 115 | 'th', 116 | 'thead', 117 | 'time', 118 | 'title', 119 | 'tr', 120 | 'u', 121 | 'ul', 122 | 'var', 123 | 'video', 124 | ]; 125 | 126 | /** List of all known HTML tags */ 127 | export const allKnownTags = [ 128 | 'area', 129 | 'base', 130 | 'br', 131 | 'col', 132 | 'embed', 133 | 'hr', 134 | 'img', 135 | 'input', 136 | 'link', 137 | 'meta', 138 | 'param', 139 | 'source', 140 | 'track', 141 | 'wbr', 142 | 143 | 'a', 144 | 'abbr', 145 | 'acronym', 146 | 'address', 147 | 'article', 148 | 'aside', 149 | 'audio', 150 | 'b', 151 | 'basefont', 152 | 'bdi', 153 | 'bdo', 154 | 'big', 155 | 'blockquote', 156 | 'body', 157 | 'button', 158 | 'canvas', 159 | 'caption', 160 | 'center', 161 | 'cite', 162 | 'code', 163 | 'colgroup', 164 | 'data', 165 | 'datalist', 166 | 'dd', 167 | 'del', 168 | 'details', 169 | 'dfn', 170 | 'dialog', 171 | 'div', 172 | 'dl', 173 | 'dt', 174 | 'em', 175 | 'fieldset', 176 | 'figcaption', 177 | 'figure', 178 | 'footer', 179 | 'form', 180 | 'h1', 181 | 'h2', 182 | 'h3', 183 | 'h4', 184 | 'h5', 185 | 'h6', 186 | 'head', 187 | 'header', 188 | 'html', 189 | 'i', 190 | 'iframe', 191 | 'ins', 192 | 'kbd', 193 | 'label', 194 | 'legend', 195 | 'li', 196 | 'main', 197 | 'map', 198 | 'mark', 199 | 'meter', 200 | 'nav', 201 | 'noscript', 202 | 'object', 203 | 'ol', 204 | 'optgroup', 205 | 'option', 206 | 'output', 207 | 'p', 208 | 'picture', 209 | 'pre', 210 | 'progress', 211 | 'q', 212 | 'rp', 213 | 'rt', 214 | 'ruby', 215 | 's', 216 | 'samp', 217 | 'script', 218 | 'section', 219 | 'select', 220 | 'small', 221 | 'span', 222 | 'strong', 223 | 'style', 224 | 'sub', 225 | 'summary', 226 | 'sup', 227 | 'svg', 228 | 'table', 229 | 'tbody', 230 | 'td', 231 | 'template', 232 | 'textarea', 233 | 'tfoot', 234 | 'th', 235 | 'thead', 236 | 'time', 237 | 'title', 238 | 'tr', 239 | 'u', 240 | 'ul', 241 | 'var', 242 | 'video', 243 | ]; 244 | 245 | export const storableTags = ['style', 'script']; 246 | 247 | /** @export @type {TagName} */ 248 | export type TagName = ValidTagName | (string & {}); 249 | /** @type {ValidTagName} */ 250 | export type ValidTagName = 251 | | 'a' 252 | | 'abbr' 253 | | 'acronym' 254 | | 'address' 255 | | 'area' 256 | | 'article' 257 | | 'aside' 258 | | 'audio' 259 | | 'b' 260 | | 'base' 261 | | 'basefont' 262 | | 'bdi' 263 | | 'bdo' 264 | | 'big' 265 | | 'blockquote' 266 | | 'body' 267 | | 'br' 268 | | 'button' 269 | | 'canvas' 270 | | 'caption' 271 | | 'center' 272 | | 'cite' 273 | | 'code' 274 | | 'col' 275 | | 'colgroup' 276 | | 'data' 277 | | 'datalist' 278 | | 'dd' 279 | | 'del' 280 | | 'details' 281 | | 'dfn' 282 | | 'dialog' 283 | | 'div' 284 | | 'dl' 285 | | 'dt' 286 | | 'em' 287 | | 'embed' 288 | | 'fieldset' 289 | | 'figcaption' 290 | | 'figure' 291 | | 'footer' 292 | | 'form' 293 | | 'h1' 294 | | 'h2' 295 | | 'h3' 296 | | 'h4' 297 | | 'h5' 298 | | 'h6' 299 | | 'head' 300 | | 'header' 301 | | 'hr' 302 | | 'html' 303 | | 'i' 304 | | 'iframe' 305 | | 'img' 306 | | 'input' 307 | | 'ins' 308 | | 'kbd' 309 | | 'label' 310 | | 'legend' 311 | | 'li' 312 | | 'link' 313 | | 'main' 314 | | 'map' 315 | | 'mark' 316 | | 'meta' 317 | | 'meter' 318 | | 'nav' 319 | | 'noscript' 320 | | 'object' 321 | | 'ol' 322 | | 'optgroup' 323 | | 'option' 324 | | 'output' 325 | | 'p' 326 | | 'param' 327 | | 'picture' 328 | | 'pre' 329 | | 'progress' 330 | | 'q' 331 | | 'rp' 332 | | 'rt' 333 | | 'ruby' 334 | | 's' 335 | | 'samp' 336 | | 'script' 337 | | 'section' 338 | | 'select' 339 | | 'selfClosingTagName' 340 | | 'small' 341 | | 'span' 342 | | 'strong' 343 | | 'style' 344 | | 'sub' 345 | | 'summary' 346 | | 'sup' 347 | | 'svg' 348 | | 'table' 349 | | 'tbody' 350 | | 'td' 351 | | 'template' 352 | | 'textarea' 353 | | 'tfoot' 354 | | 'th' 355 | | 'thead' 356 | | 'time' 357 | | 'title' 358 | | 'track' 359 | | 'tr' 360 | | 'u' 361 | | 'ul' 362 | | 'var' 363 | | 'video' 364 | | 'wbr'; 365 | -------------------------------------------------------------------------------- /src/custom-types/types.ts: -------------------------------------------------------------------------------- 1 | import { Tag } from '../tag'; 2 | import { TagBuilder } from '../tag-builder'; 3 | import { CssProperty } from './css-properties'; 4 | import { PickPropertyValues } from './css-property-values'; 5 | 6 | export type StyleMap = { [key in CssProperty]?: PickPropertyValues }; 7 | export type NestedStyleMap = { 8 | [key in CssProperty]?: PickPropertyValues | StyleMap; 9 | }; 10 | export type StyleSet = { [key: string]: NestedStyleMap }; 11 | 12 | export enum AttachMode { 13 | none, 14 | body, 15 | html, 16 | head, 17 | } 18 | export type ValidTagChild = string | Tag | { [key: string]: StyleMap } | TagBuilder | ((_: string) => void); 19 | 20 | export type TagMeta = { 21 | storage: any; 22 | selfClosing: boolean; 23 | storesChildren: boolean; 24 | }; 25 | 26 | export type FindBy = (tag: Tag) => boolean; 27 | export type State = {} & {}; 28 | export type StateProxy = T & { state: boolean }; 29 | 30 | export type GlobalStuff = Function | string; 31 | export type HoboContext = { 32 | attachedTag: Tag | undefined | null; 33 | attachedTagStack: Tag[]; 34 | globalStuff: GlobalStuff[]; 35 | }; 36 | 37 | export type HtmlEventType = 'click' | (string & {}); 38 | -------------------------------------------------------------------------------- /src/generation/css-generator.ts: -------------------------------------------------------------------------------- 1 | import { NestedStyleMap, StyleMap } from '../custom-types/types'; 2 | import { camelToDash, isObject } from '../util'; 3 | 4 | export class CssGenerator { 5 | generateCss(styleSheet: { [key: string]: NestedStyleMap } | { [key: string]: NestedStyleMap }[]) { 6 | let stylesheets = styleSheet instanceof Array ? styleSheet : [styleSheet]; 7 | let generatedCss = ''; 8 | 9 | for (const sheet of stylesheets) { 10 | for (const key in sheet) { 11 | generatedCss += this.generateBlock(key, sheet[key]); 12 | } 13 | } 14 | return generatedCss; 15 | } 16 | 17 | generateBlock(selector: string, style: NestedStyleMap) { 18 | let blocks = this.generateBlockContent(selector, style); 19 | return blocks.join(''); 20 | } 21 | 22 | generateBlockContent(selector: string, style: NestedStyleMap): string[] { 23 | let inside = ''; 24 | let blocks = []; 25 | 26 | for (const key in style) { 27 | if (isObject(style[key])) { 28 | blocks.push(this.generateBlockContent(selector + key, style[key])); 29 | } 30 | else if (style[key]) { 31 | inside += this.generateStyle(key, style[key] as string); 32 | } 33 | } 34 | 35 | blocks.unshift(`${selector}{${inside}}`); 36 | 37 | return blocks; 38 | } 39 | 40 | generateInline(style: StyleMap): string { 41 | let inside = ''; 42 | for (const key in style) { 43 | if (style[key]) { 44 | inside += this.generateStyle(key, style[key] as string); 45 | } 46 | } 47 | return inside; 48 | } 49 | 50 | generateStyle(name: string, value: string) { 51 | return `${camelToDash(name)}:${value};`; 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /src/generation/html-generator.ts: -------------------------------------------------------------------------------- 1 | import { Tag } from '../tag'; 2 | import { CssGenerator } from './css-generator'; 3 | import { justFnBody } from '../util'; 4 | import { TagBuilder } from '../tag-builder'; 5 | 6 | export class HtmlGenerator { 7 | private cssGenerator = new CssGenerator(); 8 | public beautifyCss = true; 9 | 10 | /** Generate html from the tag provided */ 11 | generateHtml(rootTag: Tag): string { 12 | let generatedHtml = this._generateTag(rootTag); 13 | return generatedHtml; 14 | } 15 | 16 | private _generateTag(tag: Tag) { 17 | if (tag.tagName == 'style') { 18 | return this._createTag(tag, this.cssGenerator.generateCss(tag._meta.storage)); 19 | } 20 | 21 | if (tag.tagName == 'script') { 22 | return this._createTag(tag, this._generateScriptContent(tag._meta.storage)); 23 | } 24 | 25 | let inside = ''; 26 | 27 | for (const child of tag.children) { 28 | let effectiveChild = child; 29 | 30 | if (child instanceof TagBuilder) { 31 | effectiveChild = child.b(); 32 | } 33 | 34 | if (effectiveChild instanceof Tag) { 35 | inside += this._generateTag(effectiveChild); 36 | } else { 37 | inside += child; 38 | } 39 | } 40 | 41 | return this._createTag(tag, inside); 42 | } 43 | 44 | private _createTag(tag: Tag, inside: string) { 45 | const attributesString = this._generateAttributeString(tag); 46 | let openTag = [tag.tagName, attributesString].filter((n) => n).join(' '); 47 | 48 | if (tag._meta.selfClosing) { 49 | return `<${openTag}/>`; 50 | } 51 | 52 | return `<${openTag}>${inside}`; 53 | } 54 | 55 | private _generateAttributeString(tag: Tag) { 56 | let attributesString = ''; 57 | 58 | if (tag.attr.id) { 59 | attributesString += this._attr('id', tag.attr.id); 60 | } 61 | 62 | attributesString += this._attr('class', tag.attr.className.raw()); 63 | attributesString += this._generateInlineStyle(tag); 64 | 65 | for (const key in tag.attr.additionalAttributes) { 66 | attributesString += this._attr(key, tag.attr.additionalAttributes[key]); 67 | } 68 | 69 | return attributesString; 70 | } 71 | 72 | private _generateInlineStyle(tag: Tag) { 73 | let styleContent = this.cssGenerator.generateInline(tag.attr.style.styles); 74 | 75 | return this._attr('style', styleContent); 76 | } 77 | 78 | private _generateScriptContent(storage: Function | Function[]): string { 79 | let scriptContent = ''; 80 | 81 | if (storage instanceof Function) { 82 | scriptContent += justFnBody(storage); 83 | } else if (storage instanceof Array) { 84 | for (const fn of storage) { 85 | scriptContent += this._generateScriptContent(fn); 86 | } 87 | } 88 | 89 | return scriptContent; 90 | } 91 | 92 | private _attr(name: string, value: string) { 93 | if (!value) return ''; 94 | return `${name}="${value}"`; 95 | } 96 | } 97 | -------------------------------------------------------------------------------- /src/hobo.ts: -------------------------------------------------------------------------------- 1 | import { Tag } from './tag'; 2 | export { Tag } from './tag'; 3 | import { HtmlGenerator } from './generation/html-generator'; 4 | import { AttachMode, HoboContext, ValidTagChild } from './custom-types/types'; 5 | import { builders as tagBuilders, TagBuilder, PickArgType } from './tag-builder'; 6 | export { TagBuilder } from './tag-builder'; 7 | import { TagName, ValidTagName } from './custom-types/tag-names'; 8 | 9 | export let _context: HoboContext = { 10 | attachedTag: null, 11 | attachedTagStack: [], 12 | globalStuff: [], 13 | }; 14 | 15 | /** 16 | * Creates an HTML document, with a head and body tags. 17 | * You can pass in the AttachMode to attach to different tags. 18 | */ 19 | export function doc(pageTitle: string = 'New Hobo Document', mode: AttachMode = AttachMode.body) { 20 | const dhead = builders.head 21 | .aa('lang', 'en') 22 | .build( 23 | builders.meta.addAttr('charset', 'UTF-8'), 24 | builders.meta.setAttr({ name: 'viewport', content: 'width=device-width, initial-scale=1.0' }), 25 | builders.title(pageTitle), 26 | ); 27 | const dbody = builders.body.build(); 28 | const doc = builders.html.build(dhead, dbody); 29 | 30 | switch (mode) { 31 | case AttachMode.html: 32 | attach(doc); 33 | break; 34 | case AttachMode.head: 35 | attach(doc.findByTagName('head') as Tag); // We know there is a head tag 36 | break; 37 | case AttachMode.body: 38 | attach(doc.findByTagName('body') as Tag); // We know there is a body tag 39 | break; 40 | case AttachMode.none: 41 | break; 42 | } 43 | 44 | return { doc, head: dhead, body: dbody }; 45 | } 46 | 47 | /** 48 | * Attach a given tag to the current context. 49 | * When you attach a tag, this tag will be the "root" for any tag created without a parent. 50 | * 51 | * * If there is not attached tag, it will be attached 52 | * * If there is already a tag attached, it will store the previous tag 53 | * and will set the new tag as the root. After finishing using the tag as the root, you can call `@detach` 54 | * and return to the previous root tag. 55 | * 56 | * This is used to remove clutter and reduntancies when creating hobo docs. 57 | * Like this: 58 | * 59 | * @example 60 | * Simple example with only 1 attach 61 | * ```ts 62 | * const parent = doc(); 63 | * attach(parent); 64 | * 65 | * div(); 66 | * p(); 67 | * ``` 68 | * The `div` and `p` tags will be automatically added as child of `parentDiv` 69 | * 70 | * @example 71 | * Example attaching and detaching 72 | * ```ts 73 | * const parent = doc(); 74 | * attach(parent); 75 | * 76 | * div(); 77 | * p(); 78 | * let d1 = div(); 79 | * attach(d1); 80 | * // All the p tags will be added to `d1` 81 | * p(); 82 | * p(); 83 | * p(); 84 | * // remember to call detach when you want to go back to the previous root tag 85 | * detach(); 86 | * ``` 87 | * 88 | * @param {Tag} tag 89 | */ 90 | export function attach(tag: Tag) { 91 | if (_context.attachedTag) { 92 | _context.attachedTagStack.push(_context.attachedTag); 93 | } 94 | _context.attachedTag = tag; 95 | } 96 | 97 | /** 98 | * Detached the currently attached tag, and pops back to the previously attached tag. 99 | * If there are no stored tags, it will clear the attached tag. 100 | * You will need to handle the consecuent created tags. 101 | */ 102 | export function detach() { 103 | if (_context.attachedTagStack.length > 0) { 104 | _context.attachedTag = _context.attachedTagStack.pop(); 105 | } else { 106 | _context.attachedTag = null; 107 | } 108 | } 109 | 110 | function makeAttachable(builder: TagBuilder): TagBuilder & { a: TagBuilder; attach: TagBuilder } { 111 | const attachFn = () => { 112 | if (_context.attachedTag) { 113 | return builder.p(_context.attachedTag); 114 | } 115 | return builder; 116 | }; 117 | 118 | Object.defineProperty(builder, 'a', { 119 | get: attachFn, 120 | }); 121 | 122 | Object.defineProperty(builder, 'attach', { 123 | get: attachFn, 124 | }); 125 | 126 | return builder; 127 | } 128 | 129 | 130 | type BuilderFunctions = { 131 | [key in ValidTagName]: ((...children: PickArgType) => Tag) & TagBuilder & { a: TagBuilder }; 132 | } & { 133 | tag: (tagName: TagName, ...children: PickArgType) => TagBuilder; 134 | }; 135 | 136 | const exportedTagBuilders: BuilderFunctions = {} as BuilderFunctions; 137 | 138 | for (let key in tagBuilders) { 139 | exportedTagBuilders[key] = makeAttachable(tagBuilders[key]); 140 | } 141 | 142 | const _generator = new HtmlGenerator(); 143 | 144 | /** Converts's the Tag tree into a html string */ 145 | export function generate(root: Tag) { 146 | return _generator.generateHtml(root); 147 | } 148 | 149 | /** 150 | * TagBuilders for each known tag. From `div` to `acronym` 151 | */ 152 | export const builders = exportedTagBuilders; 153 | -------------------------------------------------------------------------------- /src/style.ts: -------------------------------------------------------------------------------- 1 | import { CssProperty } from './custom-types/css-properties'; 2 | import { PickPropertyValues } from './custom-types/css-property-values'; 3 | 4 | /** 5 | * Represents a set of styles 6 | */ 7 | export class StyleSet { 8 | styles: { [key in CssProperty]?: PickPropertyValues } = {}; 9 | 10 | copy() { 11 | const newStyles = new StyleSet(); 12 | newStyles.styles = { ...this.styles }; 13 | return newStyles; 14 | } 15 | 16 | /** Set a single style */ 17 | set(key: T, value: PickPropertyValues): StyleSet { 18 | this.styles[key] = value as any; 19 | return this; 20 | } 21 | 22 | /** Remove styles */ 23 | remove(...styles: string[]): StyleSet { 24 | for (const sn of styles) { 25 | delete this.styles[sn]; 26 | } 27 | return this; 28 | } 29 | 30 | /** Check if a style is set */ 31 | has(key: string): boolean { 32 | return key in this.styles; 33 | } 34 | } 35 | -------------------------------------------------------------------------------- /src/tag-builder.ts: -------------------------------------------------------------------------------- 1 | import { AttrSet } from './attributes'; 2 | import { ClassName } from './class-name'; 3 | import { Tag } from './tag'; 4 | import { CssProperty } from './custom-types/css-properties'; 5 | import { PickPropertyValues } from './custom-types/css-property-values'; 6 | import { TagName, ValidTagName, allKnownTags, selfClosingTags, storableTags } from './custom-types/tag-names'; 7 | import { StyleMap, StyleSet, TagMeta, ValidTagChild } from './custom-types/types'; 8 | 9 | class ExFunc extends Function { 10 | private __self__: any; 11 | 12 | constructor() { 13 | super('...args', 'return this.__self__.__call__(...args)'); 14 | var self = this.bind(this); 15 | this.__self__ = self; 16 | return self; 17 | } 18 | 19 | /* istanbul ignore next */ 20 | __call__(...children: ValidTagChild[]) {} 21 | } 22 | 23 | /** 24 | * TagBuilder class, used to build tags of course. 25 | */ 26 | export class TagBuilder extends ExFunc { 27 | tagName: TagName; 28 | children: ValidTagChild[] = []; 29 | 30 | /** Get the tag className */ 31 | get className() { 32 | return this.attr.className; 33 | } 34 | 35 | /** Get the tag id */ 36 | get tagId() { 37 | return this.attr.id; 38 | } 39 | 40 | /** 41 | * Do not modify directly, use helper methods in the tag instead. 42 | */ 43 | attr: AttrSet = new AttrSet(); 44 | 45 | _parent: Tag; 46 | _meta: TagMeta = { 47 | selfClosing: false, 48 | storesChildren: false, 49 | storage: false, 50 | }; 51 | 52 | constructor(tagName: TagName, ...children: ValidTagChild[]) { 53 | super(); 54 | this.setTagName(tagName); 55 | this.children.push(...children); 56 | } 57 | 58 | copy() { 59 | const newBuilder = new TagBuilder(this.tagName, ...this.children); 60 | newBuilder.attr = this.attr.copy(); 61 | newBuilder._parent = this._parent; 62 | newBuilder._meta = { ...this._meta, ...newBuilder._meta }; 63 | return newBuilder; 64 | } 65 | 66 | /** Sets and validates the tag name */ 67 | setTagName(name: string) { 68 | this.tagName = this.sanitizeTagName(name); 69 | this.validateTagName(this.tagName); 70 | this._meta = { 71 | ...this._meta, 72 | ...this.getMetaForTag(this.tagName), 73 | }; 74 | return this; 75 | } 76 | 77 | /** 78 | * Shorthand for `.build` method 79 | * Build the tag with additional children 80 | */ 81 | b(...children: ValidTagChild[]): Tag { 82 | return this.build(...children); 83 | } 84 | 85 | /** 86 | * Build the tag with additional children 87 | */ 88 | build(...children: ValidTagChild[]): Tag { 89 | return this.__call__(...children); 90 | } 91 | 92 | __call__(...children: ValidTagChild[]): Tag { 93 | let tagChildren = [...this.children, ...children]; 94 | 95 | if (this._meta.storesChildren) { 96 | this._meta.storage = children; 97 | tagChildren = []; 98 | } 99 | 100 | const built = new Tag(this.tagName, tagChildren, this.attr, this._meta); 101 | 102 | if (this._parent) { 103 | this._parent.children.push(built); 104 | } 105 | return built; 106 | } 107 | 108 | /** Attach to the currently attached tag in the global hobo context */ 109 | /* istanbul ignore next */ 110 | get a() { 111 | return this.copy(); 112 | } 113 | 114 | /** Same as .a - Attach to the currently attached tag in the global hobo context */ 115 | /* istanbul ignore next */ 116 | get attach() { 117 | return this.copy(); 118 | } 119 | 120 | /** Set the parent of the tag. If a parent is set, this tag will be added as a child when built */ 121 | p(parent: Tag) { 122 | const copy = this.copy(); 123 | copy._parent = parent; 124 | return copy; 125 | } 126 | 127 | /** 128 | * Set the id of the tag 129 | * Can't be empty 130 | */ 131 | id(newId: T extends '' ? never : T) { 132 | const copy = this.copy(); 133 | copy.attr.id = newId; 134 | return copy; 135 | } 136 | 137 | /** replaces the children of this tag with the provided string */ 138 | text(content: string) { 139 | const copy = this.copy(); 140 | copy.children = [content]; 141 | return copy; 142 | } 143 | 144 | /** 145 | * Adds tags as children if the tag can have children. 146 | * For example, if tag is `img` there's no need to add the childre as they will not be generated. 147 | */ 148 | append(...tags: ValidTagChild[]) { 149 | if (this._meta.selfClosing) { 150 | return this; 151 | } 152 | 153 | const copy = this.copy(); 154 | copy.children.push(...tags.map((c) => (c instanceof TagBuilder ? c.b() : c))); 155 | 156 | return copy; 157 | } 158 | 159 | /** Set the children of this tag. Replaces any current children */ 160 | setChildren(children: ValidTagChild[]) { 161 | const copy = this.copy(); 162 | copy.children = children; 163 | return copy; 164 | } 165 | 166 | /** Store metadata inside tag. Internal method, you won't need this */ 167 | store(o: any) { 168 | const copy = this.copy(); 169 | copy._meta.storage = o; 170 | return copy; 171 | } 172 | 173 | /** 174 | * cm = modify 175 | * calls `fn` with the tag, and returns the tag 176 | * 177 | * usefull to change a tag while maintaing chaning 178 | * 179 | * @example 180 | * ```ts 181 | * div().m(t => t.className.add("Container")) 182 | * .div("I'm a child!"), 183 | * ``` 184 | * @example 185 | * ```ts 186 | * div([ 187 | * p("Child1").m(t => t.className.add("child-1")), 188 | * p("Child1").m(t => t.className.add("child-2")) 189 | * ]) 190 | * ``` 191 | */ 192 | m(fn: (tag: TagBuilder) => void) { 193 | return this.mod(fn); 194 | } 195 | 196 | /** 197 | * calls `fn` with the tag, and returns the tag 198 | * 199 | * usefull to change a tag while maintaing chaning 200 | * 201 | * @example 202 | * ```ts 203 | * div().m(t => t.className.add("Container")) 204 | * .div("I'm a child!"), 205 | * ``` 206 | * @example 207 | * ```ts 208 | * div([ 209 | * p("Child1").mod(t => t.className.add("child-1")), 210 | * p("Child1").mod(t => t.className.add("child-2")) 211 | * ]) 212 | * ``` 213 | */ 214 | mod(fn: (tag: TagBuilder) => void) { 215 | const copy = this.copy(); 216 | fn(copy); 217 | return copy; 218 | } 219 | 220 | /** 221 | * Shortcut for method .modClass 222 | * 223 | * Modifies the classnames of a tag. Similar to the `.mod` or `.m` methods 224 | * but it passes the className instead of the complete tag. 225 | * 226 | * Retuns a new TagBuilder 227 | */ 228 | mc(arg0: (c: ClassName) => void) { 229 | return this.modClass(arg0); 230 | } 231 | 232 | /** 233 | * Modifies the classnames of a tag. Similar to the `.mod` or `.m` methods 234 | * but it passes the className instead of the complete tag. 235 | * 236 | * Retuns a new TagBuilder 237 | * 238 | * @example 239 | * ```ts 240 | * div( 241 | * p("Child1").modClass(c => c.add("child-1")), 242 | * p("Child1").modClass(c => c.add("child-2")) 243 | * ) 244 | * ``` 245 | */ 246 | modClass(arg0: (c: ClassName) => void) { 247 | const copy = this.copy(); 248 | return copy.m((t) => arg0(t.className)); 249 | } 250 | 251 | /** 252 | * Shorthand for .addClass method. 253 | * Adds classNames to this TagBuilder, and returns a new TagBuilder 254 | */ 255 | ac(...classNames: string[]) { 256 | return this.addClass(...classNames); 257 | } 258 | 259 | /** 260 | * Adds classNames to this TagBuilder, and returns a new TagBuilder 261 | */ 262 | addClass(...classNames: string[]) { 263 | const copy = this.copy(); 264 | copy.className.add(...classNames); 265 | return copy; 266 | } 267 | 268 | /** 269 | * Shorthand for .rmClass method. 270 | * Removes classNames from this TagBuilder, and returns a new TagBuilder 271 | */ 272 | rc(...classNames: string[]) { 273 | return this.rmClass(...classNames); 274 | } 275 | 276 | /** 277 | * Removes classNames from this TagBuilder, and returns a new TagBuilder 278 | */ 279 | rmClass(...classNames: string[]) { 280 | const copy = this.copy(); 281 | copy.className.remove(...classNames); 282 | return copy; 283 | } 284 | 285 | /** 286 | * Shorthand for .addAttr method. 287 | * Adds attribute, and returns a new TagBuilder 288 | */ 289 | aa(key: string, value: string) { 290 | return this.addAttr(key, value); 291 | } 292 | 293 | /** Add one attribute, and return a new TagBuilder */ 294 | addAttr(key: string, value: string) { 295 | const copy = this.copy(); 296 | copy.attr.set(key, value); 297 | return copy; 298 | } 299 | 300 | /** 301 | * Shorthand for .setAttr method. 302 | * Sets multiple atributes at once, and returns a new TagBuilder 303 | */ 304 | sa(attributes: { [key: string]: string }) { 305 | return this.setAttr(attributes); 306 | } 307 | 308 | /** Sets multiple atributes at once, and returns a new TagBuilder */ 309 | setAttr(attributes: { [key: string]: string }) { 310 | const copy = this.copy(); 311 | copy.attr.additionalAttributes = { 312 | ...copy.attr.additionalAttributes, 313 | ...attributes, 314 | }; 315 | return copy; 316 | } 317 | 318 | /** 319 | * Shorthand for .removeAttr method. 320 | * Removes attribute from this TagBuilder, and returns a new TagBuilder 321 | */ 322 | ra(...attr: string[]) { 323 | return this.rmAttr(...attr); 324 | } 325 | 326 | /** 327 | * Removes attribute from this TagBuilder, and returns a new TagBuilder 328 | */ 329 | rmAttr(...attr: string[]) { 330 | const copy = this.copy(); 331 | copy.attr.remove(...attr); 332 | return copy; 333 | } 334 | 335 | /** 336 | * Shorthand for .addStyle method 337 | * Adds a single style, and returns a new TagBuilder 338 | */ 339 | as(key: T, value: PickPropertyValues) { 340 | return this.addStyle(key, value); 341 | } 342 | 343 | /** 344 | * Adds a single style, and returns a new TagBuilder 345 | */ 346 | addStyle(key: T, value: PickPropertyValues) { 347 | const copy = this.copy(); 348 | copy.attr.style.set(key, value); 349 | return copy; 350 | } 351 | 352 | /** 353 | * Shorthand for .setStyles method. 354 | * Adds style from object, and returns a new TagBuilder 355 | */ 356 | ss(styles: StyleMap) { 357 | return this.setStyles(styles); 358 | } 359 | 360 | /** Adds style from object, and returns a new TagBuilder */ 361 | setStyles(styles: StyleMap) { 362 | const copy = this.copy(); 363 | copy.attr.style.styles = { 364 | ...copy.attr.style.styles, 365 | ...styles, 366 | }; 367 | return copy; 368 | } 369 | 370 | /** 371 | * Shorthand for .removeStyles method. 372 | * Removes styles from this TagBuilder, and returns a new TagBuilder 373 | */ 374 | rs(...styleNames: string[]) { 375 | return this.rmStyle(...styleNames); 376 | } 377 | 378 | /** 379 | * Removes styles from this TagBuilder, and returns a new TagBuilder 380 | */ 381 | rmStyle(...styleNames: string[]) { 382 | const copy = this.copy(); 383 | copy.attr.style.remove(...styleNames); 384 | return copy; 385 | } 386 | 387 | // Utilities 388 | private getMetaForTag(tagName: TagName): TagMeta { 389 | return { 390 | selfClosing: this.isSelfClosingTag(tagName), 391 | storesChildren: this.isStorableTag(tagName), 392 | storage: null, 393 | }; 394 | } 395 | 396 | private isSelfClosingTag(tagName: string) { 397 | return selfClosingTags.includes(tagName); 398 | } 399 | private isStorableTag(tagName: string) { 400 | return storableTags.includes(tagName); 401 | } 402 | private validateTagName(tagName: TagName) { 403 | if (!/[a-zA-Z_][a-z-A-Z0-9_]*/.test(tagName)) { 404 | throw new Error(`Invalid tag name "${tagName}"`); 405 | } 406 | } 407 | private sanitizeTagName(tagName: TagName) { 408 | return tagName.replace(/[^\w\d-_]/, ''); 409 | } 410 | } 411 | 412 | function tagBuilder(tagName: TagName, ...children: ValidTagChild[]): TagBuilder { 413 | return new TagBuilder(tagName, ...children); 414 | } 415 | const tagNames = allKnownTags; 416 | 417 | export type PickArgType = T extends 'style' ? StyleSet[] : ValidTagChild[]; 418 | 419 | type BuilderFunctions = { 420 | [key in ValidTagName]: ((...children: PickArgType) => Tag) & TagBuilder; 421 | } & { 422 | /** 423 | * Create a new TagBuilder with specified tagName 424 | * @example 425 | * ```ts 426 | * tag('uknown-tag'); 427 | * ``` 428 | */ 429 | tag: (tagName: TagName, ...children: PickArgType) => TagBuilder; 430 | }; 431 | 432 | let fns: Partial = { 433 | tag: tagBuilder, 434 | }; 435 | 436 | for (let tname of tagNames) { 437 | fns[tname] = tagBuilder(tname); 438 | } 439 | 440 | export const builders = fns; 441 | -------------------------------------------------------------------------------- /src/tag.ts: -------------------------------------------------------------------------------- 1 | import { TagName } from './custom-types/tag-names'; 2 | import { AttrSet } from './attributes'; 3 | import { FindBy, TagMeta, ValidTagChild } from './custom-types/types'; 4 | import { TagBuilder } from './tag-builder'; 5 | 6 | /** 7 | * Represents an html tag 8 | */ 9 | export class Tag { 10 | tagName: TagName; 11 | children: ValidTagChild[] = []; 12 | attr: AttrSet = new AttrSet(); 13 | 14 | _meta: TagMeta = { 15 | selfClosing: false, 16 | storesChildren: false, 17 | storage: false, 18 | }; 19 | 20 | get className() { 21 | return this.attr.className; 22 | } 23 | get tagId() { 24 | return this.attr.id; 25 | } 26 | 27 | constructor(tagName: TagName, children: ValidTagChild[], attr: AttrSet, meta: TagMeta) { 28 | this.tagName = tagName; 29 | this.children = children; 30 | this.attr = attr; 31 | this._meta = meta; 32 | } 33 | 34 | /** Append children */ 35 | append(child: ValidTagChild) { 36 | if (child instanceof TagBuilder) child = child.b(); 37 | this.children.push(child); 38 | } 39 | 40 | /** Find a child by tag name */ 41 | findByTagName(targetTagName: TagName): Tag | null { 42 | return this.findOneBy((t) => t.tagName == targetTagName); 43 | } 44 | 45 | /** Find a child by custom test */ 46 | findOneBy(test: FindBy): Tag | null { 47 | const stack: Tag[] = []; 48 | stack.push(this); 49 | 50 | while (stack.length > 0) { 51 | let tag: Tag = stack.pop() as Tag; 52 | 53 | if (test(tag)) { 54 | return tag; 55 | } else if (tag.children && tag.children.length) { 56 | for (let ii = 0; ii < tag.children.length; ii += 1) { 57 | if (this.children[ii] instanceof Tag) { 58 | stack.push(tag.children[ii] as Tag); 59 | } 60 | } 61 | } 62 | } 63 | 64 | return null; 65 | } 66 | } 67 | -------------------------------------------------------------------------------- /src/util.ts: -------------------------------------------------------------------------------- 1 | /** Receives a function, and returns just the body of the function as a string */ 2 | export function justFnBody(fn: Function) { 3 | let fnStr = fn.toString(); 4 | fnStr = fnStr.replace(/^(.*{)/, ''); 5 | fnStr = fnStr.replace(/}$/, ''); 6 | fnStr = fnStr.replace(/^\(.*\)\s?=>\s?{/, ''); 7 | return fnStr.trim(); 8 | } 9 | export const replaceDoubleQuotes = (str: string)=>str.replace(/"/g, "'"); 10 | export const generateId = () => `_hb${s4() + s4()}`; 11 | export const camelToDash = (str) => str.replace(/([A-Z])/g, (val) => `-${val.toLowerCase()}`); 12 | export const dashToCamel = (str) => str.replace(/(\-[a-z])/g, (val) => val.toUpperCase().replace('-', '')); 13 | export function isObject(obj: any): boolean { 14 | return typeof obj === 'object' && !(obj instanceof Array); 15 | } 16 | const s4 = ()=>(((1 + Math.random()) * 0x10000) | 0).toString(16).substring(1); -------------------------------------------------------------------------------- /tests/unit/css-generator.test.ts: -------------------------------------------------------------------------------- 1 | import { CssGenerator } from '../../src/generation/css-generator'; 2 | 3 | describe('CssGenerator', () => { 4 | const generator = new CssGenerator(); 5 | 6 | it('generateCss() basic works', async () => { 7 | const generated = generator.generateCss({ 8 | body: {}, 9 | }); 10 | expect(generated).toEqual('body{}'); 11 | }); 12 | 13 | it('generateCss() with styles', async () => { 14 | const generated = generator.generateCss({ 15 | body: { 16 | color: 'red', 17 | flexDirection: 'column', 18 | }, 19 | }); 20 | expect(generated).toEqual('body{color:red;flex-direction:column;}'); 21 | }); 22 | 23 | it('generateCss() with nested styles', async () => { 24 | const generated = generator.generateCss({ 25 | body: { 26 | color: 'red', 27 | flexDirection: 'column', 28 | ':hover': { color: 'blue' }, 29 | }, 30 | }); 31 | expect(generated).toEqual('body{color:red;flex-direction:column;}body:hover{color:blue;}'); 32 | }); 33 | }); 34 | -------------------------------------------------------------------------------- /tests/unit/hobo.test.ts: -------------------------------------------------------------------------------- 1 | import { AttachMode } from '../../src/custom-types/types'; 2 | import { _context, attach, builders, detach, doc, generate } from '../../src/hobo'; 3 | import { Tag } from '../../src/tag'; 4 | 5 | describe('TagBuilder', () => { 6 | it('doc works', async () => { 7 | const d = doc(); 8 | expect('doc' in d).toBeTruthy(); 9 | expect('head' in d).toBeTruthy(); 10 | expect('body' in d).toBeTruthy(); 11 | 12 | expect(d.doc).toBeInstanceOf(Tag); 13 | expect(d.head).toBeInstanceOf(Tag); 14 | expect(d.body).toBeInstanceOf(Tag); 15 | }); 16 | 17 | it('doc(AttachMode.html) works', async () => { 18 | _context.attachedTag = null; // Reset 19 | _context.attachedTagStack = []; 20 | const d = doc('title', AttachMode.html); 21 | expect(_context.attachedTag).toBe(d.doc); 22 | }); 23 | 24 | it('doc(AttachMode.head) works', async () => { 25 | _context.attachedTag = null; // Reset 26 | _context.attachedTagStack = []; 27 | const d = doc('title', AttachMode.head); 28 | expect(_context.attachedTag).toBe(d.head); 29 | }); 30 | 31 | it('doc(AttachMode.body) works', async () => { 32 | _context.attachedTag = null; // Reset 33 | _context.attachedTagStack = []; 34 | const d = doc('title', AttachMode.body); 35 | expect(_context.attachedTag).toBe(d.body); 36 | }); 37 | 38 | it('doc(AttachMode.none) works', async () => { 39 | _context.attachedTag = null; // Reset 40 | _context.attachedTagStack = []; 41 | const d = doc('title', AttachMode.none); 42 | expect(_context.attachedTag).toBe(null); 43 | }); 44 | 45 | it('attach() works', async () => { 46 | _context.attachedTag = null; // Reset 47 | _context.attachedTagStack = []; 48 | const d = builders.div.b(); 49 | attach(d); 50 | expect(_context.attachedTag).toEqual(d); 51 | 52 | const child = builders.div.a.b(); 53 | 54 | expect(_context.attachedTag.children).toContain(child); 55 | }); 56 | 57 | it('attach() works when there are other attached tags', async () => { 58 | _context.attachedTag = null; // Reset 59 | _context.attachedTagStack = []; 60 | const d = builders.div.b(); 61 | attach(d); 62 | const d1 = builders.div(); 63 | attach(d1); 64 | expect(_context.attachedTag).toEqual(d1); 65 | expect(_context.attachedTagStack).toEqual([d]); 66 | 67 | detach(); 68 | expect(_context.attachedTag).toEqual(d); 69 | expect(_context.attachedTagStack).toEqual([]); 70 | detach(); 71 | expect(_context.attachedTag).toEqual(null); 72 | expect(_context.attachedTagStack).toEqual([]); 73 | }); 74 | 75 | it('generate() works', async () => { 76 | const d = builders.div.b(); 77 | expect(generate(d)).toEqual('
'); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /tests/unit/html-generator.test.ts: -------------------------------------------------------------------------------- 1 | import { HtmlGenerator } from '../../src/generation/html-generator'; 2 | import { builders } from '../../src/hobo'; 3 | 4 | describe('HtmlGenerator', () => { 5 | const generator = new HtmlGenerator(); 6 | 7 | it('generateHtml() basic tag works', async () => { 8 | const d1 = builders.div(); 9 | const generated = generator.generateHtml(d1); 10 | expect(generated).toEqual('
'); 11 | }); 12 | 13 | it('generateHtml() style tag works', async () => { 14 | const d1 = builders.style({ '.class': { color: 'red' } }); 15 | const generated = generator.generateHtml(d1); 16 | expect(generated).toEqual(''); 17 | }); 18 | 19 | it('generateHtml() script tag works', async () => { 20 | const d1 = builders.script(() => { 21 | console.log('hey'); 22 | }); 23 | const generated = generator.generateHtml(d1); 24 | expect(generated).toEqual(""); 25 | }); 26 | 27 | it('generateHtml() works with nested childs', async () => { 28 | const d1 = builders.div(builders.span(), builders.p('Some string')); 29 | const generated = generator.generateHtml(d1); 30 | expect(generated).toEqual(`

Some string

`); 31 | }); 32 | 33 | it('generateHtml() works with nested tag builders', async () => { 34 | const d1 = builders.div(builders.span(), builders.p); 35 | const generated = generator.generateHtml(d1); 36 | expect(generated).toEqual(`

`); 37 | }); 38 | 39 | it('generateHtml() works for self closing tags', async () => { 40 | expect(generator.generateHtml(builders.img.b())).toEqual(''); 41 | }); 42 | 43 | it('generateHtml() works with attributes', async () => { 44 | expect(generator.generateHtml(builders.img.aa('src', 'test.com').b())).toEqual(''); 45 | }); 46 | 47 | it('generateHtml() works with class names', async () => { 48 | expect(generator.generateHtml(builders.img.ac('src').b())).toEqual(''); 49 | }); 50 | 51 | it('generateHtml() works with id', async () => { 52 | expect(generator.generateHtml(builders.img.id('src').b())).toEqual(''); 53 | }); 54 | 55 | it('generateHtml() works with inline styles', async () => { 56 | expect(generator.generateHtml(builders.img.as('color', 'red').b())).toEqual(''); 57 | }); 58 | }); 59 | -------------------------------------------------------------------------------- /tests/unit/miscelaneous.test.ts: -------------------------------------------------------------------------------- 1 | import { AttrSet } from '../../src/attributes'; 2 | import { StyleSet } from '../../src/style'; 3 | 4 | describe('Misc tests', () => { 5 | it('attr.has()', () => { 6 | const attr = new AttrSet(); 7 | attr.set('one', 'two'); 8 | expect(attr.has('one')).toEqual(true); 9 | expect(attr.has('two')).toEqual(false); 10 | }); 11 | it('style.has()', () => { 12 | const style = new StyleSet(); 13 | style.set('one', 'two'); 14 | expect(style.has('one')).toEqual(true); 15 | expect(style.has('two')).toEqual(false); 16 | }); 17 | }); -------------------------------------------------------------------------------- /tests/unit/tag-builder.test.ts: -------------------------------------------------------------------------------- 1 | import { allKnownTags, closingTags, selfClosingTags } from '../../src/custom-types/tag-names'; 2 | import { builders } from '../../src/hobo'; 3 | import { Tag } from '../../src/tag'; 4 | import { builders as tagBuilders, TagBuilder } from '../../src/tag-builder'; 5 | const { tag } = builders; 6 | 7 | describe('TagBuilder', () => { 8 | it('creates correctly', async () => { 9 | const builder = tag('custom-tag'); 10 | expect(builder).toBeDefined(); 11 | expect(builder.tagName).toEqual('custom-tag'); 12 | }); 13 | 14 | it('all tags exist as builders', async () => { 15 | for (let tagName of allKnownTags) { 16 | expect(tagName in builders).toBeTruthy(); 17 | } 18 | }); 19 | 20 | it('selfClosing tags set correct metadata', async () => { 21 | for (let tagName of selfClosingTags) { 22 | const builder = builders[tagName] as TagBuilder; 23 | expect(builder._meta.selfClosing).toEqual(true); 24 | } 25 | }); 26 | 27 | it('nonSelfClosing tags set correct metadata', async () => { 28 | for (let tagName of closingTags) { 29 | const builder = builders[tagName] as TagBuilder; 30 | expect(builder._meta.selfClosing).toEqual(false); 31 | } 32 | }); 33 | 34 | it('.className returns className', async () => { 35 | const someTag = builders.div; 36 | expect(someTag.className).toBe(someTag.attr.className); 37 | }); 38 | 39 | it('.tagId returns id', async () => { 40 | const someTag = builders.div.id('aaa'); 41 | expect(someTag.attr.id).toEqual('aaa'); 42 | expect(someTag.tagId).toEqual('aaa'); 43 | }); 44 | 45 | it('.b builds tag', async () => { 46 | const someTag = builders.div.id('aaa'); 47 | const built = someTag.b(); 48 | 49 | expect(built.tagName).toEqual('div'); 50 | expect(built.tagId).toEqual('aaa'); 51 | expect(built).toBeInstanceOf(Tag); 52 | }); 53 | 54 | it('.a returns builder', async () => { 55 | const someTag = (tagBuilders.div as TagBuilder).a; 56 | expect(someTag).toBeInstanceOf(TagBuilder); 57 | }); 58 | 59 | it('.p sets parent', async () => { 60 | const papa = builders.div(); 61 | const someTag = builders.div.p(papa); 62 | 63 | expect(someTag._parent).toEqual(papa); 64 | }); 65 | 66 | it('.text replaces children with given text', async () => { 67 | const papa = builders.div.text('Some text'); 68 | expect(papa.b().children).toEqual(['Some text']); 69 | }); 70 | 71 | it('.append adds children', async () => { 72 | const child = builders.div(); 73 | const papa = builders.div.append(child); 74 | 75 | expect(papa.children).toEqual([child]); 76 | }); 77 | 78 | it('.append does not add children for selfClosingTags', async () => { 79 | const child = builders.div(); 80 | const papa = builders.img.append(child); 81 | 82 | expect(papa.children).toEqual([]); 83 | }); 84 | 85 | 86 | it('.append works with TagBuilder', async () => { 87 | 88 | const papa = builders.div.append(builders.div); 89 | 90 | expect(papa.children).toEqual([builders.div()]); 91 | }); 92 | 93 | it('.setChildren does not add children for selfClosingTags', async () => { 94 | const el = builders.div.append('Initial child'); 95 | const child = builders.a(); 96 | const papa = el.setChildren([builders.a()]); 97 | 98 | expect(papa.children).toEqual([builders.a()]); 99 | }); 100 | 101 | it('.store stores', async () => { 102 | const el = builders.div.store('some data'); 103 | expect(el._meta.storage).toEqual('some data'); 104 | }); 105 | 106 | it('.m works', async () => { 107 | const el = builders.div.m((b) => b.attr.className.add('one')); 108 | expect(el.b().attr.className.classNames).toContain('one'); 109 | }); 110 | 111 | it('.mc works', async () => { 112 | const el = builders.div.mc((c) => c.add('one')); 113 | expect(el.b().attr.className.classNames).toContain('one'); 114 | }); 115 | 116 | it('.ac works', async () => { 117 | const el = builders.div.ac('one'); 118 | expect(el.b().attr.className.classNames).toContain('one'); 119 | }); 120 | 121 | it('.rc works', async () => { 122 | const el = builders.div.ac('one').rc('one'); 123 | expect(el.b().attr.className.classNames).not.toContain('one'); 124 | }); 125 | 126 | it('.aa works', async () => { 127 | const el = builders.div.aa('one', 'two').b(); 128 | expect('one' in el.attr.additionalAttributes).toEqual(true); 129 | expect(el.attr.additionalAttributes['one']).toEqual('two'); 130 | }); 131 | 132 | it('.am works', async () => { 133 | const el = builders.div.sa({ one: 'two' }).b(); 134 | expect('one' in el.attr.additionalAttributes).toEqual(true); 135 | expect(el.attr.additionalAttributes['one']).toEqual('two'); 136 | }); 137 | 138 | it('.ra works', async () => { 139 | const el = builders.div.aa('one', 'two'); 140 | el.ra('one'); 141 | expect(el.b().attr.additionalAttributes).not.toContain('one'); 142 | }); 143 | 144 | it('.as works', async () => { 145 | const el = builders.div.as('color', 'red').b(); 146 | expect('color' in el.attr.style.styles).toEqual(true); 147 | expect(el.attr.style.styles['color']).toEqual('red'); 148 | }); 149 | 150 | it('.ss works', async () => { 151 | const el = builders.div.ss({ color: 'red' }).b(); 152 | expect('color' in el.attr.style.styles).toEqual(true); 153 | expect(el.attr.style.styles['color']).toEqual('red'); 154 | }); 155 | 156 | it('.rs works', async () => { 157 | const el = builders.div.ss({ color: 'red' }).rs('color').b(); 158 | expect('color' in el.attr.style.styles).toEqual(false); 159 | }); 160 | 161 | it('Error for invalid tag!', async () => { 162 | expect(() => { 163 | builders.tag('123&'); 164 | }).toThrow(); 165 | }); 166 | }); 167 | -------------------------------------------------------------------------------- /tests/unit/tag.test.ts: -------------------------------------------------------------------------------- 1 | import { AttrSet } from '../../src/attributes'; 2 | import { Tag } from '../../src/tag'; 3 | import { builders } from '../../src/tag-builder'; 4 | 5 | describe('TagBuilder', () => { 6 | it('creates correctly', async () => { 7 | const tag = new Tag('custom-tag', [], new AttrSet(), { 8 | selfClosing: false, 9 | storesChildren: false, 10 | storage: false, 11 | }); 12 | expect(tag).toBeDefined(); 13 | expect(tag.tagName).toEqual('custom-tag'); 14 | expect(tag.children).toEqual([]); 15 | }); 16 | 17 | it('.className returns className', async () => { 18 | const attrSet = new AttrSet(); 19 | const tag = new Tag('custom-tag', [], attrSet, { 20 | selfClosing: false, 21 | storesChildren: false, 22 | storage: false, 23 | }); 24 | expect(tag.className).toEqual(attrSet.className); 25 | }); 26 | 27 | it('.append adds children', async () => { 28 | const tag1 = new Tag('custom-tag', [], new AttrSet(), { 29 | selfClosing: false, 30 | storesChildren: false, 31 | storage: false, 32 | }); 33 | const tag2 = new Tag('custom-tag2', [], new AttrSet(), { 34 | selfClosing: false, 35 | storesChildren: false, 36 | storage: false, 37 | }); 38 | 39 | tag1.append(tag2); 40 | 41 | expect(tag1.children).toEqual([tag2]); 42 | }); 43 | 44 | it('.append works with TagBuilder', async () => { 45 | const tag1 = new Tag('custom-tag', [], new AttrSet(), { 46 | selfClosing: false, 47 | storesChildren: false, 48 | storage: false, 49 | }); 50 | const d = builders.div.ac('aa'); 51 | 52 | tag1.append(d); 53 | 54 | expect(tag1.children[0]).toBeInstanceOf(Tag); 55 | }); 56 | 57 | it('.findByTagName works', async () => { 58 | const tag1 = new Tag('custom-tag', [], new AttrSet(), { 59 | selfClosing: false, 60 | storesChildren: false, 61 | storage: false, 62 | }); 63 | const d = builders.div.ac('aa'); 64 | tag1.append(d); 65 | 66 | const found = tag1.findByTagName('div'); 67 | expect(found).toEqual(d.b()); 68 | }); 69 | 70 | it('.findByTagName returns null if not find', async () => { 71 | const tag1 = new Tag('custom-tag', [], new AttrSet(), { 72 | selfClosing: false, 73 | storesChildren: false, 74 | storage: false, 75 | }); 76 | const found = tag1.findByTagName('div'); 77 | expect(found).toEqual(null); 78 | }); 79 | }); 80 | -------------------------------------------------------------------------------- /tsconfig.build.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "es6", 4 | "moduleResolution": "Node", 5 | "target": "ES6", 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "sourceMap": true, 9 | "noImplicitAny": false, 10 | "lib": [ 11 | "es2015", 12 | "dom" 13 | ], 14 | "outDir": "dist", 15 | "types": [ 16 | "node", 17 | "jest" 18 | ], 19 | "declarationDir": "dist/types" 20 | }, 21 | "include": [ 22 | "src/**/*" 23 | ], 24 | "lib": [ 25 | "es2015", 26 | "dom" 27 | ] 28 | } -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "CommonJS", 4 | "moduleResolution": "Node", 5 | "target": "ES2015", 6 | "declaration": true, 7 | "esModuleInterop": true, 8 | "sourceMap": true, 9 | "noImplicitAny": false, 10 | "lib": [ 11 | "es2015", 12 | "dom" 13 | ], 14 | "outDir": "dist", 15 | "types": [ 16 | "node", 17 | "jest" 18 | ], 19 | "declarationDir": "dist/types" 20 | }, 21 | "include": [ 22 | "src/**/*" 23 | ], 24 | "lib": [ 25 | "es2015", 26 | "dom" 27 | ] 28 | } --------------------------------------------------------------------------------