@magicdiff --git a/.gitignore b/.gitignore index d999c68a..d5201df9 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,4 @@ npm-debug.log yarn-error.log coverage .DS_Store +.tmp diff --git a/.travis.yml b/.travis.yml index fff65042..f0d7c1ad 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,7 +1,7 @@ language: node_js node_js: - - "13.5.0" + - "14.2.0" cache: npm @@ -11,7 +11,7 @@ os: env: matrix: - - NODE_VERSION="13" + - NODE_VERSION="14.2.0" matrix: fast_finish: true diff --git a/README.md b/README.md index 7cf78454..863179dc 100644 --- a/README.md +++ b/README.md @@ -3,11 +3,11 @@ this is the @magic core library. [![NPM version][npm-image]][npm-url] +[![License][license-image]][license-url] [![Node version][node-image]][node-url] [![Linux Build Status][travis-image]][travis-url] [![Windows Build Status][appveyor-image]][appveyor-url] [![Coverage Status][coveralls-image]][coveralls-url] -[![Greenkeeper badge][greenkeeper-image]][greenkeeper-url] [![Known Vulnerabilities][snyk-image]][snyk-url] builds a webapp with deeply nested component structure and globally/locally shared state and actions. @@ -18,152 +18,816 @@ to get more information, head to the [docs / demo](https://magic.github.io/core/) other related github organizations: -* [@magic-modules](https://magic-modules.github.io) - building blocks for your app -* [@magic-themes](https://magic-themes.github.io) - themes for your app -* [@magic-libraries](https://magic-libraries.github.io) - client libraries + +- [@magic-modules](https://magic-modules.github.io) - building blocks for your app +- [@magic-themes](https://magic-themes.github.io) - themes for your app +- [@magic-libraries](https://magic-libraries.github.io) - client libraries #### changelog ##### 0.0.1 + first commit ##### 0.0.2 + use @magic npm packages instead of github for installs ##### 0.0.3 -* update dependencies -* make @magic actually work in the client if @magic-modules/gdpr is not installed. + +- update dependencies +- make @magic actually work in the client if @magic-modules/gdpr is not installed. ##### 0.0.4 -* dev server now correctly redirects urls if the trailing slash is missing -* html and markdown files can include state and variables + +- dev server now correctly redirects urls if the trailing slash is missing +- html and markdown files can include state and variables ##### 0.0.5 -* @magic-themes can now add variables to the css colors without overwriting the default colors -* @magic-modules/messages gets autoloaded if it is installed. -* components now can be nested without array. div([p('text')]) can also be div(p('text')) -* Link({ to: '#hashLink' }) is local now -* very early blog implementation. it works, but the archive pages need more work. + +- @magic-themes can now add variables to the css colors without overwriting the default colors +- @magic-modules/messages gets autoloaded if it is installed. +- components now can be nested without array. div([p('text')]) can also be div(p('text')) +- Link({ to: '#hashLink' }) is local now +- very early blog implementation. it works, but the archive pages need more work. ##### 0.0.6 + always delete .tmp dir ##### 0.0.7 + fix 404 page regression -##### 0.0.8 - unreleased -* blog/year/month archive pages are not 404'ing. -* blog archive pages now have links in them if needed +##### 0.0.8 + +- blog/year/month archive pages are not 404'ing. +- blog archive pages now have links in them if needed (year page links to each month archive page) -* blog posts are wrapped in BlogPost automagically. +- blog posts are wrapped in BlogPost automagically. BlogPost adds header and description automagically. -* config.HOIST allows adding any module at the bottom of the page (gdpr, messages will use that) +- config.HOIST allows adding any module at the bottom of the page (gdpr, messages will use that) ##### 0.0.9 -* config.HOIST can be an array or a string + +- config.HOIST can be an array or a string ##### 0.0.10 -* config.HOIST defaults to empty array -* app.actions.gdpr will only be added if Gdpr is included in config.HOIST + +- config.HOIST defaults to empty array +- app.actions.gdpr will only be added if Gdpr is included in config.HOIST ##### 0.0.11 -* allow arbitrary, module and page level init effects for initialState + +- allow arbitrary, module and page level init effects for initialState ##### 0.0.12 -* add SkipLink for screen readers. hash links to page start -* libraries can now be named with a - in their name and get camelCased + +- add SkipLink for screen readers. hash links to page start +- libraries can now be named with a - in their name and get camelCased ##### 0.0.13 -* svg tags are actually usable now. except color-profile and switch. + +- svg tags are actually usable now. except color-profile and switch. ##### 0.0.14 -* update @magic/css which updates postcss -* color theme vars now export grey and bluegrey. copies gray and bluegray. -* config.BABEL is now being used for babel build -* config.THEME_VARS can be a function. gets passed { colors } if it is a function. + +- update @magic/css which updates postcss +- color theme vars now export grey and bluegrey. copies gray and bluegray. +- config.BABEL is now being used for babel build +- config.THEME_VARS can be a function. gets passed { colors } if it is a function. ##### 0.0.15 + regression: babel should minify files on build. ##### 0.0.16 -* modules/MenuItem: only add submenu ul if submenu has items.length > 0 -* link menu parent .to only gets prepended to link.to if link.to starts with - or #, not if it starts with /. -* add modules/MenuLink. use it in modules/MenuItem + +- modules/MenuItem: only add submenu ul if submenu has items.length > 0 +- link menu parent .to only gets prepended to link.to if link.to starts with - or #, not if it starts with /. +- add modules/MenuLink. use it in modules/MenuItem ##### 0.0.17 -* **commonjs support is gone.** -* **minimum node version bumped to 13.5.0** -* modules/Router.mjs: Link module now accepts action as prop -* modules/Router.mjs: scroll page back to top if the clicked link points to current page root + +- **commonjs support is gone.** +- **minimum node version bumped to 13.5.0** +- modules/Router.mjs: Link module now accepts action as prop +- modules/Router.mjs: scroll page back to top if the clicked link points to current page root ##### 0.0.18 + do not error if docs/sri-hashes.json does not exist ##### 0.0.19 + **minimum node version of dependencies bumped to 13.5.0** ##### 0.0.20 -* update dependencies -* update theme to show list-style for ordered lists (ol) -* better error handling -* remove lib/fs, use @magic/fs everywhere -* is @magic/types for isUpperCase and isLowerCase + +- update dependencies +- update theme to show list-style for ordered lists (ol) +- better error handling +- remove lib/fs, use @magic/fs everywhere +- is @magic/types for isUpperCase and isLowerCase ##### 0.0.21 + add viewport and ie=edge html meta tags. ##### 0.0.22 + tasks/prepare/css: also load themes directly from node_modules/${theme_name}. ##### 0.0.23 -* all hail eris! -* css styles now inherit THEME_VARS from all possible source before transpiling styles. -* colors.bluegrey now is alias for colors.bluegray instead of undefined. -* client side routing scrolls to top in all cases, but is not animated. + +- all hail eris! +- css styles now inherit THEME_VARS from all possible source before transpiling styles. +- colors.bluegrey now is alias for colors.bluegray instead of undefined. +- client side routing scrolls to top in all cases, but is not animated. scroll-behavior + window.scroll() = bug ##### 0.0.24 -* Router: set document.title on pushstate if possible -* Seo: add image for social previews + +- Router: set document.title on pushstate if possible +- Seo: add image for social previews ##### 0.0.25 -* fix: do not error if none of the pages has state.title set. + +- fix: do not error if none of the pages has state.title set. ##### 0.0.26 + config.IGNORED_STATIC is an array. can include path ('/file.ext') or extension ('.ext') strings. ##### 0.0.27 + do not create sri-hashes for config.IGNORED_STATIC files ##### 0.0.28 + themes can export Modules now, which can overwrite builtins. ##### 0.0.29 + fix some edgecases with exported theme modules ##### 0.0.30 + local config.THEME_VARS get deep merged over theme provided THEME_VARS now. ##### 0.0.31 + if multiple themes are installed, the last imported theme will overwrite earlier imported themes and their vars. ##### 0.0.32 + fix race condition in case of multiple themes. modules did not always get overwritten in the correct order. ##### 0.0.33 + MenuItem: fix link handling for rooted links. initial / will not be removed from links. -##### 0.0.34 - unreleased -* themes: merge state, actions, effects, and subscriptions into app. -* maybeApp import: only suppress error if it is E_MODULE_NOT_FOUND. +##### 0.0.34 + +- themes: merge state, actions, effects, and subscriptions into app. +- maybeApp import: only suppress error if it is E_MODULE_NOT_FOUND. + +##### 0.0.35 + +fix: hash routing in webkit works (chromium, brave tested) + +##### 0.0.36 + +modules/app: correctly merge maybeApp and theme state, actions, effects and subscriptions. + +##### 0.0.37 + +- config: IGNORED_STATIC can be a string. +- config: items in IGNORED_STATIC do not have to start with a dot. will be added if it is missing. +- babel errors tell us to run `npm run format` to find the source of the error. Sourcemaps tbd +- build worker process should always exit with process.exit(1), not panic. + +##### 0.0.38 + +- babel: do not mangle toplevel names. broke builds, adds 1kb+ to js. investigate, exterminate. + +##### 0.0.39 + +- only load Modules from theme if their names are CamelCased. +- handle state as a function in all imports, call it with config param +- @magic/transform handles @magic-modules now, which makes pure markdown pages possible. + +##### 0.0.40 + +fix: HOISTing broke in 0.0.39 + +##### 0.0.41 + +hoisting happens in the footer. + +##### 0.0.42 + +fix: theme modules are now getting imported again. + +##### 0.0.43 + +update @magic/transmute: @magic-modules in markdown are no longer wrapped in p tags + +##### 0.0.44 + +add Credit module + +##### 0.0.45 + +add b, strong, i, and em styles (bold and italic) + +##### 0.0.46 + +- no more race conditions when importing multiple themes. +- Logo link works again. + +##### 0.0.47 + +modules/app: page.state gets inherited properly, markdown pages can see global state now. + +##### 0.0.48 + +import originalState from transmuted bundle. + +##### 0.0.49 + +FIX: process would hang if a markdown or html page had no state. + +##### 0.0.50 + +document.title does not error in client if only one page exists. + +##### 0.0.51 + +##### FULL @magic-module api available from markdown. + +- nested state variables in template strings eg {{state.test.deep.var}} now work. +- replace parse5 with posthtml. +- custom markdown and custom html renderers added. +- @magic-modules in markdown and html can have keys that are arrays or objects now. +- state variables in template strings can be arrays (eg state.description). + will be join(' ')ed + +##### 0.0.52 + +fix: remove admin for now + +##### 0.0.53 + +- transmute now returns empty strings instead of empty arrays and objects. + +##### 0.0.54 + +- transmute/render/markdown.codespan: do not show line numbers for code spans. +- do not pass modules to transmute. + +##### 0.0.55 + +@magic/transmute now correctly unescapes quot entities. + +##### 0.0.56 + +- Credits module does not error if style variables are not set. + +##### 0.0.57 + +tasks/prepare: module.state extends, but does not overwrite app.state when importing modules + +##### 0.0.58 + +update @magic/transmute, which now actually replaces all occurrences of html entities in a string, +not just the first one. + +##### 0.0.59 + +- tasks/serve/handler: use array to collect req.body data. + strings break unicode characters if the chunks split a character on transit +- only create sri-hashes for static files in conf.INCLUDED_HASH_EXTENSIONS +- update dependencies => minimist vuln + +##### 0.0.60 + +- modules/Router/Link.onclick: now also resets window.location.hash if the hash did not change. +- tasks/serve/handler: response data chunks are now always buffers. + +##### 0.0.61 + +update babel and @magic/transmute + +##### 0.0.62 + +update babel jsx and @magic/transmute + +##### 0.0.63 + +modules/Seo now allows arbitrary header fields. + +##### 0.0.64 + +modules/Header passes state.theme to Logo + +##### 0.0.65 + +magic can now serve a log of mime-types. see @magic/mime-types for full list + +##### 0.0.66 + +update dependencies + +##### 0.0.67 + +update @magic/transmute + +##### 0.0.68 + +@magic/transmute does not add p tags around Link and Img elements. + +##### 0.0.69 + +update dependencies + +##### 0.0.70 + +update dependencies + +##### 0.0.71 + +- update dependencies +- sri-hashes.json is a flat object: key = path, value = hash + +##### 0.0.72 + +- bump required node version +- update dependencies +- cli help expanded +- cli flags get passed to config +- better lib handling + +##### 0.0.73 + +update dependencies. + +##### 0.0.74 + +- src/index: export runCluster +- pkg: add type: module, add main field + +##### 0.0.75 + +- magic build --no-minify flag now works +- themes/theme_name.mjs is now a valid theme path +- apis can be served by @grundstein/gas and created on page, app and module level + +##### 0.0.76 + +- update dependencies +- hyperapp-render is a proper module now + +##### 0.0.77 + +- add config.API_DIR. load all files in that dir and use their default exports as lambdas. +- update babel build task + +##### 0.0.78 + +update babel + +##### 0.0.79 + +- FIX: sri-hashes for magic.js and magic.css get added to html again. +- update dependencies + +##### 0.0.80 + +update @magic/css + +##### 0.0.81 + +- Img module now has loading=lazy as default prop. +- update @magic/css + +##### 0.0.82 + +FIX: regression, reenable custom state for pages. + +##### 0.0.83 + +update lodash to mitigate development environment security issue. + +##### 0.0.84 + +update deps + +##### 0.0.85 + +icon sizes can be customized + +##### 0.0.86 + +update icon size usage + +##### 0.0.87 + +themes/reset.css: add icon animation for hoisted icons + +##### 0.0.88 + +update dependencies + +##### 0.0.89 + +update babel + +##### 0.0.90 + +- remove unused imports +- formatting +- split tasks/globals into separate files +- update api to allow self-contained lambdas which load their dependencies using await import +- update dependencies + +##### 0.0.91 + +update dependencies + +##### 0.0.92 + +update dependencies + +##### 0.0.93 + +update dependencies + +##### 0.0.94 + +update dependencies + +##### 0.0.95 + +- serve/prepareApi: remove console.log +- allow addition of third party scripts using ADD_SCRIPTS config variable +- update dependencies + +##### 0.0.96 + +git tagged but not published + +##### 0.0.97 + +- do not error if page is not a javascript module +- update dependencies +- add app.build task +- update dependencies + +##### 0.0.98 + +- if the app.mjs import fails because one of the app dependencies is missing, + throw an error instead of silently not importing the app. +- update dependencies + +##### 0.0.99 + +- use @swc/core for dev builds to make them 10 times faster. +- update dependencies +- only duplicate unique page.state into app.state, 20-30% bundle size reduction. +- cluster/build task recovers from build errors, no restart needed. + +##### 0.0.100 + +- config.API_DIR now actually gets used +- api routes now 404 if they do not exist (redirected to WEB_ROOT before) +- update deps + +##### 0.0.101 + +- Router: on navigation scroll to top works in newer firefox, tradeoff: probably not in older. +- npm i -g magic now works and loads local node_modules as well as global + +##### 0.0.102 + +- further fixes regarding npm i -g magic +- bump required node version to 14.15.4 +- update dependencies + +##### 0.0.103 + +update dependencies + +##### 0.0.104 + +update dependencies + +##### 0.0.105 + +- Header, Menu, MenuItem and Logo arguments updated + +##### 0.0.106 + +update dependencies (marked) + +##### 0.0.107 + +- update dependencies +- swc now handles links in pages correctly (dev) +- babel now handles links in pages correctly (production) +- prepare task now handles all urls in the state object, + no need to handle it in babel/swc or in modules. + +##### 0.0.108 + +debump hyperapp-render from 4.0.0 to 3.5.0 + +##### 0.0.109 + +- fix error in modules/Logo +- tasks/prepare/stateLinks now handles #hash and -expand links without parents +- check for broken links on build and dev +- better link handling, should now include all images and links in pages too + +##### 0.0.110 + +- update dependencies +- restructure most of the app tasks and remove global.config. + this will allow us to seamlessly use @magic/core in the @magic/test library +- libs can now be static function instead of imports +- export renderToString + +##### 0.0.111 + +- magic.init can now be an actual array of code, instead of string only +- update dependencies +- more robust checkLinks testing + +##### 0.0.112 + +- libraries can be actual code or a string pointing to a file +- update hyperapp and use 2.0.12 +- update hyperapp-render to 4.0.0 +- update dependencies +- href link checkers handle mailto: links +- import hyperapp memo instead of Lazy +- starting to remove usages of config without explicit destructuring +- replaceSlashSlash all urls in robots.txt and sitemap.xml +- add ADD_TAGS to append arbitrary html after the #magic root +- the watch task can now watch files +- the watch task watches the config file + +##### 0.0.113 + +- FIX: tasks/prepare/page: change variable name to avoid "access before init" error when pages do not build successfully. +- update dependencies + +##### 0.0.114 + +- to keep dev builds fast, external links only get checked on build. +- deprecate config.mjs in favor of magic.(m)js +- magic.js config import will now error and exit if config does not exist +- if no config file is found, output an example config +- show a warning if config.mjs is still being used +- only checkLinks external links in production env + +##### 0.0.115 + +- swc handles OptionalChainingExpression and Computed +- nicer error messages +- add Permissions-Policy interest-cohort=() to maybe avoid FLOC tracking in chrome. + +##### 0.0.116 + +- add @magic/hyperapp bundle that exports hyperapp and hyperapp-render + +##### 0.0.117 + +correctly export renderToString from index (for @magic/test) + +##### 0.0.118 + +add silent option to silence log output and apply to config. + +##### 0.0.119 + +- update dependencies +- conf.ADD_STATIC can be used to import static files from multiple directories +- component handles prerendered hyperapp objects correctly again. +- html pages have their href and src elements prefixed with WEB_ROOT + +##### 0.0.120 + +- runConfig: bail early if config file is not found +- lib/findConfigFile: only process.exit if args.silent is false + +##### 0.0.121 + +update dependencies + +##### 0.0.122 + +- update @babel/preset-env to avoid security issue + +- config: add PREPEND_SCRIPT and APPEND_SCRIPT to allow adding js script tags before and after the magic js +- config: add PREPEND_JS and APPEND_JS to allow adding js contents before and after the magic js, directly into magic.js +- config: add PREPEND_CSS and APPEND_CSS to allow adding css before and after magic js +- config: add PREPEND_TAGS and APPEND_TAGS to allow adding html tags before and after #magic tag + +##### 0.0.123 + +- BUG: do not try to return an undefined hashes var if PREPEND_SCRIPTS or APPEND_SCRIPTS is empty +- swc: handle EmptyStatement +- update dependencies + +##### 0.0.124 + +make sure all app.static[key] keys start with a slash (config.ADD_STATIC) + +##### 0.0.125 + +- cluster.watch now also watches config.DIR.STATIC directories +- update dependencies + +##### 0.0.126 + +update dependencies + +##### 0.0.127 + +update dependencies to avoid circular dependency in @magic/types + +##### 0.0.128 + +update dependencies + +##### 0.0.129 + +update dependencies + +##### 0.0.130 + +update dependencies + +##### 0.0.131 + +update dependencies + +##### 0.0.132 + +update dependencies + +##### 0.0.133 + +- update dependencies +- theme: move css from html to body to prevent duplicate scrollbars in blink/webkit + +##### 0.0.134 + +- update dependencies +- config.DIR.LIB is an array and imports lib files from each of them + +##### 0.0.135 + +- theme imports now throw MODULE_NOT_FOUND errors in their internal imports, they got swallowed before +- CHECK_PROPS can be silenced +- update dependencies + +##### 0.0.136 + +update dependencies + +##### 0.0.137 + +- allow .js files for modules and libraries +- update dependencies + +##### 0.0.138 + +- better encoding for ' characters in state strings +- update dependencies + +##### 0.0.139 + +- update dependencies + +##### 0.0.140 + +- update dependencies +- @magic/test can now test @magic/core again + +##### 0.0.141 + +update dependencies + +##### 0.0.142 + +update dependencies + +##### 0.0.143 + +update dependencies + +##### 0.0.144 + +- update dependencies +- remove babel, swc can handle it by now + (bundle js size 88k babel to 90k swc for this page, 18 byte difference if using zopfli). +- config.BABEL deprecation warning +- app.helpers deprecation warning +- propTypes no longer need to be namespaced + + ``` + // Module.mjs + export const propTypes = [//...propTypes] + + // will turn into: + export const propTypes = { Module: [//..propTypes]} + ``` + +##### 0.0.145 + +- update dependencies +- swc will prepend links in the return statements of modules with WEB_ROOT if needed. +- CHECK_PROPS supports oneOf and someOf. added some tests. + +##### 0.0.146 + +- update dependencies +- FIX from 0.0.145: CHECK_PROPS now can handle logging and errors. +- FIX from 0.0.145: swc can handle string literals in img src and srcset parameters + +##### 0.0.147 + +swc will now remove CHECK_PROPS from magic.js production bundles. + +##### 0.0.148 + +- update dependencies +- checkLinks supports NO_CHECK_LINKS_LIST in magic.js, which allows us to ignore known broken links, for example when pages block our http requests. + +##### 0.0.149 + +- update dependencies +- better error message if a module can not be required +- global html tags will always be defined in order to make sure they exist before requiring other Modules + +##### 0.0.150 + +update dependencies + +##### 0.0.151 + +- update dependencies +- FIX: bug when assigning local state of modules, only the last item was written, now they all get added to state.componentname. +- swc: add AwaitExpression and set swc.jsc.target to es2017, leading to async-await making it into the final bundle. +- module.global can now also be called module.globals +- tasks/prepare/js now makes sure that `import`ed modules work. +- swc: add ForOfStatement + +##### 0.0.152 + +- downgrade swc + +##### 0.0.153 + +- magic now seems to work on windows. + +##### 0.0.154 + +- update dependencies +- link WEB_ROOT prefixing ignores Identifier, TemplateLiteral and BinaryExpression ast nodes, they are dynamic and should not be manipulated +- Img module will not automatically set role="presentation" when alt="", props.loading will always be set to lazy unless provided +- CheckLinks will not error if TemplateLiterals are used in the url +- Magic exports replaceSlashSlash from library +- modules/app will not error if state of a page is undefined in src/app.mjs + +##### 0.0.155 + +- reduce @swc/core version back to 1.3.41 to prevent errors when transpiling + +##### 0.0.156 + +update dependencies + +##### 0.0.157 + +update dependencies + +##### 0.0.158 + +update @magic/test and fix spec tests + +##### 0.0.159 - unreleased -##### 0.0.35 - unreleased ... [npm-image]: https://img.shields.io/npm/v/@magic/core.svg [npm-url]: https://www.npmjs.com/package/@magic/core +[license-image]: https://img.shields.io/npm/l/@magic/core.svg +[license-url]: https://www.npmjs.com/package/@magic/core [node-image]: https://img.shields.io/node/v/@magic/core/latest [node-url]: https://www.npmjs.com/package/@magic/core [travis-image]: https://img.shields.io/travis/com/magic/core/master @@ -172,7 +836,5 @@ MenuItem: fix link handling for rooted links. initial / will not be removed from [appveyor-url]: https://ci.appveyor.com/project/magic/core/branch/master [coveralls-image]: https://coveralls.io/repos/github/magic/core/badge.svg [coveralls-url]: https://coveralls.io/github/magic/core -[greenkeeper-image]: https://badges.greenkeeper.io/magic/core.svg -[greenkeeper-url]: https://badges.greenkeeper.io/magic/core.svg [snyk-image]: https://snyk.io/test/github/magic/core/badge.svg [snyk-url]: https://snyk.io/test/github/magic/core diff --git a/appveyor.yml b/appveyor.yml index f5ac4c99..cda15e87 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -11,7 +11,7 @@ matrix: install: # Build - - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild 13.5.0) $env:platform + - ps: Update-NodeJsInstallation (Get-NodeJsLatestBuild 14.2.0) $env:platform - npm install test_script: diff --git a/docs/404.html b/docs/404.html index 18a31b49..26c73704 100644 --- a/docs/404.html +++ b/docs/404.html @@ -1 +1 @@ -
@magic
@magicmagic concepts. These are the building blocks of every module in a magic app
modules are the main building block of magic.
a page is a module, a button is a module, a link is a module, an image is a module. a @magic app contains modules containing modules that contain modules. this can lead to inception.
state is a javascript object.
state can be mutated by actions or effects.
every rendering step, the state determines the output of the views
actions are an object containing functions
those functions get the state and their props and may return a new full, but changed, state.
effects are an object containing functions, just like actions.
they behave like actions, get a state and props and may return a new full, but changed, state.
the big difference? effects may be impure and trigger sideeffects outside of hyperapp.
views render the state to html
whenever an action triggers a change in the state, this then triggers a view change.
every module can have a style object attached to it.
magic will automagically merge all styles into one global css file.
in the future, it will also remove unused styles for you.
style merge order from lowest to highest, last overwrites first:
module.style < page.style < app.style < theme.style
internally, magic uses it's own css-in-js library.
to find out more, click the following link:
@magic/cssevery module can set a global object, containing state and action properties.
every state and/or action name in the global object with a value that equals true gets merged into the main app state/actions.
this is the serverside magic.
you can define functions that get transpiled into serverside lambdas.
server side lambdas will be available for GET and/or POST requests.
the server side function signature is (req, res) => {}, as in any nodejs http server, with the addition of req.body being async => awaited before execution of the lambda.
@magic
magic concepts. these are the building blocks of every module in a magic app
modules are the main building block of magic.
a page is a module, a button is a module, a link is a module, an image is a module. +even a theme is a module.
a @magic app contains modules containing modules that contain modules. +this can lead to inception.
state is a javascript object. +state can be mutated by actions, +effects and subscriptions can call actions to change the state. +every rendering frame, the state determines the output of the html views.
const state = {shown: false,count: 0,}
actions are an object containing functions
those functions get the state and their props and may return a new full, but changed, state.
const actions = {toggleModal: state => {return {...state,shown: !state.shown,}},calculator: {plus: state => ({ ...state, count: state.count + 1 }),minus: state => ({ ...state, count: state.count - 1 }),},}
effects are an object containing functions, just like actions.
effects can only change the state by calling actions.
effects may also be impure and trigger side-effects outside of hyperapp.
const effects = {waitASec: [state, async state => {await new Promise((r) => setTimeout(r, 1000))return actions.plus(state)}],}
at the moment, subscriptions can be defined using strings as function names.
also, if a module exports a subscriptions array, those subscriptions will be added to the app.
// in a module:export const lib = {subscribe: (dispatch, action) => {setInterval(() => {dispatch(action, { arg: Math.random() * 100 })})},}export const actions = {onSubscription: (state, e) => ({...state,arg: e.arg,})}export const subscriptions = ['lib.subscribe', 'actions.onSubscription']
views render the state to html +whenever an action triggers a change in the state, this then triggers a view change.
export const View = state => div(JSON.stringify(state))every module can export a style object. +magic will automagically merge all styles into one global css file. +in the future, it will also remove unused styles for you.
styles merge order, last overwrites first: +module.style < page.style < app.style < theme.style
this allows css to be inherited in both directions.
a module automatically creates it's own css namespace, +that same namespace can be used in page, app and theme css styles +to overwrite module specific styles.
internally, magic uses it's own css-in-js library.
to find out more, click the following link: +@magic/css
every module can set a global object, containing state and action properties.
every state and/or action name in the global object with a value that equals true gets merged into the main app state/actions, instead of into the module namespace.
// in module.mjsexport const state = {internal: 0,external: 0,}export const actions = {internal: state => ({...state,module: {internal: state.module.internal + 1,},}),external: state => ({...state,external: state.external + 1,}),}export const global = {state: {external: true},actions: {external: true,},}// in the view, we can use both the global and local actions and state variablesexport const View = state => div(['view',p({ onclick: actions.module.internal }, ['internal:', state.module.internal]),p({ onclick: actions.external }, ['external:', state.external]),])
this is the serverside magic. +you can define functions that get transpiled into serverside lambdas. +server side lambdas will be available for GET and/or POST requests.
the server side function signature is (req, res) => {}, +as it is in most nodejs http servers, +with the addition of req.body being async => awaited before execution of the lambda.
@magicThere are multiple magic files and directories.
the pages dir contains the pages of your webapp.
each page has it's own state and actions, but also inherits the global state and actions from the app and it's dependencies
/pages/index.mjs === http://mag.ic//pages/pageName.mjs === http://mag.ic/pageName//pages/page-name.mjs === http://mag.ic/page-name//pages/page_name.mjs === http://mag.ic/page_name//pages/dir/index.mjs === http://mag.ic/dir//pages/dir/name.mjs === http://mag.ic/dir/name/
export default {state: {variable: 'test',},actions: {changeVar: () => ({ variable: 'changed' }),},style: {'.cl': {color: 'green',},},View: state => div({ class: 'cl' }, ['this is the page content.',state.variable,]),}
the assets dir contains custom components of your app.
you can additionally import @magic-modules here
export default {Custom: () => div('custom component'),Pre: require('@magic-modules/pre),'}
the static dir contains all of your static assets.
every file in this directory gets copied to the public dir.
image and svg files get minified using imagemin
text and binary files get compressed using the optional node-zopfli-es (if it is installed)
the themes directory contains... themes.
a magic theme is an object of css rules, see @magic/css for more examples and documentation.
export default {'body': {color: 'blue',},}
the /app.mjs file allows you to set global state, actions, and styles
export default {state: {globalStateVar: 'globally available',},actions: {globalAction: () => ({ globalStateVar: 'overwritten.' }),},style: {'body': {color: 'green',},},}
the /config.mjs file allows you to set various aspects of your app
export default {ROOT: 'example',THEME: 'blue',WEB_ROOT: '/core/',// this option adds the// 'X-Clacks-Overhead', 'GNU Terry Pratchet'// http header// see http://www.gnuterrypratchett.com/FOR_DEATH_CAN_NOT_HAVE_HIM: true,// default CLIENT_LIB_NAME, overwrite to change names of transpiled css and js filesCLIENT_LIB_NAME: 'magic',}
@magic
There are multiple magic files and directories.
the pages dir contains the pages of your webapp.
each page has it's own state and actions, but also inherits the global state and actions from the app and it's dependencies
for the domain mag.ic:
/pages/index.mjs === http://mag.ic//pages/pageName.mjs === http://mag.ic/pageName//pages/page-name.mjs === http://mag.ic/page-name//pages/page_name.mjs === http://mag.ic/page_name//pages/dir/index.mjs === http://mag.ic/dir//pages/dir/name.mjs === http://mag.ic/dir/name/
Pages can use javascript, html or markdown
A magic javascript page is a @magic-module. The only difference is that pages get exposed via http
export const View = state =>div({ class: 'cl' }, ['this is the page content.',state.variable,])export const state = {variable: 'test',}export const actions = {changeVar: state => ({ ...state, variable: 'changed' }),}export const style = {'.cl': {color: 'green',},}
html pages can only export state and View.
---@state{"title": "html file example","description": "this module gets imported from a html file."}---<h2>${ state.title }</h4><p>{{ state.description }}</p>
markdown pages can only export state and View.
---@state {"title": "markdown file example","description": "markdown file description"}---## ${state.title}${state.description}
the assets dir contains custom components of your app.
you can import additional @magic-modules here
export default {Custom: () => div('custom component'),Pre: require('@magic-modules/pre),}
the static dir contains all of your static assets.every file in this directory gets copied to the public dir.image and svg files get minified using imagemin.
text and binary files get compressed using the optional node-zopfli-es (if it is installed)
the themes directory contains...themes.
a magic theme is an object of css rules, see @magic/css for more examples and documentation.
export default {'body': {color: 'blue',},}
the /app.mjs file allows you to set global state, actions, and styles
export const state = {globalStateVar: 'globally available',}export const actions = {globalAction: () => ({ globalStateVar: 'overwritten.' }),}export const style = {'body': {color: 'green',},}
the /config.mjs file allows you to set various aspects of your app
export default {// the local root directory of the magic appROOT: 'example',// gets suffixed to the url. in this case domain.com/core/ will be the page root.WEB_ROOT: '/core/',// the theme in use, lives in {ROOT}/assets/themes/{name} or can be installed as either '@magic-themes/{name}' or 'magic-theme-{name}'THEME: 'docs',// the full url of the page.URL: 'magic.github.io/core/',// the public dir the bundles get written toPUBLIC: 'docs',// the directory in the pages directory that holds the blog posts// if this is not set, no blog is created.BLOG_DIR: 'news',// append these modules after the app loaded and only if javascript is enabled.// can be used to add features that won't do anything without js anyways.HOIST: ['LightSwitch', 'NoSpy'],// default CLIENT_LIB_NAME, overwrite to change names of transpiled css and js filesCLIENT_LIB_NAME: 'magic',// this is set to be able to test broken link behaviour.// DO NOT ENABLE IN YOUR APP (unless you need to because reasons...)NO_CHECK_LINKS_EXIT: true,// tags written to the html before the #magic containerPREPEND_TAGS: [{ name: 'div', props: { id: 'PREPENDTag' } }],// tags written to the html after the #magic containerAPPEND_TAGS: [{ name: 'div', props: { id: 'APPENDTag' } }],// script files, embedded before the magic.js scriptPREPEND_SCRIPTS: ['/prependScript.js'],// script files, embedded after the magic.js scriptAPPEND_SCRIPTS: ['/appendScript.js'],// js files that get written directly into the magic.js script, before the magic code.PREPEND_JS: ['/prependJs.js'],// js files that get written directly into the magic.js script, after the magic code.APPEND_JS: ['/appendJs.js'],// css written into the magic.css file, before the magic css// use this to set defaults that get overwritten by the generated app cssPREPEND_CSS: ['/prependCss.css'],// css written into the magic.css file, after the magic css// use this to overwrite the generated app cssAPPEND_CSS: ['/appendCss.css'],// add additional static file directories that will get added to app.static[name]ADD_STATIC: ['node_modules/dir/name/'],// this option adds the// 'X-Clacks-Overhead', 'GNU Terry Pratchet'// http header// see http://www.gnuterrypratchett.com/FOR_DEATH_CAN_NOT_HAVE_HIM: true,}
@magicthe goal of this document is to give you a rough @magical overview.
@magic aims to make it easy to stitch together any kind of webapp. by providing simple, well documented and self contained modules, @magic makes it possible to create stunningly fast webpages with minimal cognitive overhead.
@magic does not spy.
not only do we try to be legally compliant by default, but we also aim to be ethical which means prioritizing your rights over our needs, but also means we prioritize the rights of your users over your needs. we believe that this is the best compromise.
~4 kb javascript boilerplate. usually, all the javascript in your homepage will be 30-60kb big (after unpacking), 10-30kb get transmitted from the server to the client. this documentation page you are reading loads about 15kb of javascript, which, when parsing, turns into 40kb of uncompressed javascript.
most of the functionality works without javascript, some buttons and realtime user interactions obviously won't, but @magic always tries to provide a non-javascript fallback via css.
this makes free hosting (using github or gitlab pages) possible. and it's easy.
@magic publishes to github, gitlab and any other git-pages enabled hosting service.
automagically generates serverless lambdas, derived from the @magic-modules you use in your pages. this makes visitor statistics, user authentication and authorization, chat, and all other server side services possible.
@magic
@magic/core documentation. tells you why, how and when to use @magic. also provides an overview of all @magic functionality this ecosystem provides.
the goal of this document is to give you a rough @magical overview.
@magic aims to make it easy to stitch together any kind of webapp. +by providing simple, well documented and self contained modules, +@magic makes it possible to create stunningly fast +webpages with minimal cognitive overhead.
@magic does not spy.
not only do we try to be legally compliant by default, +but we also aim to be ethical +which means prioritizing your rights over our needs, +but also means we prioritize the rights of your users over your needs. +we believe that this is the best compromise.
~4 kb javascript boilerplate. +usually, all the javascript in your homepage will be 30-60kb big (after unpacking), +10-30kb get transmitted from the server to the client. +this documentation page you are reading loads 20kb of gzipped javascript, +which, when parsing, turns into ~88kb of uncompressed javascript. +this includes all content of all subpages, no additional server requests needed for any navigations in the page.
WIP: code splitting for bigger pages.
most of the functionality works without javascript, +some buttons and realtime user interactions obviously won't, +but @magic always tries to provide a non-javascript fallback via css.
this makes free hosting (using github or gitlab pages) possible. and it's easy.
@magic publishes to github, gitlab, +and any other git-pages enabled hosting service.
automagically generates +serverless lambdas, derived from the +@magic-modules +you use in your pages.
this makes visitor statistics, user authentication and authorization, +chat, and all other server side services possible.
@magic@magic/core libraries allow you to include client side functionality in your app.
what would javascript be without the millions of dependencies that you can easily install and make the average webpage slow[ly] (pun intended) grow towards a footprint of 5 megabytes.
we think that all of that bloat is unneeded, unheeded and, frankly, not optimal.
magic has one external client side dependency, hyperapp, [~400 lines that provide our ui state machine]. thats it. and it won't change.
we also have the tendency to write libraries specialized for our usecase, see @magic/css @magic/test @magic/cli and others.
once there is a lib key in at least one component, window.lib (browser) and global.lib (nodejs) will be set, aliasing lib as a global variable in both environments
if you need libraries in multiple otherwise independent modules, it might be easier to keep your library dependencies in a central place. to achieve this, one can simply create /assets/lib.mjs and export an object from it. this object will get merged into the globalThis.lib object making it available as "lib" throughout your app.
alternatively, you can create a /assets/modules directory and place UpperCaseNamed.mjs files in there. their names will be deferred from the filename and they will be available in your app without importing them. if you do not use one of the modules, dead code elimination will simply remove it in production.
export default { name: () => {} }lib.name = () => {}all @magic-libraries/* and all npm packages starting with magic-library-* will be loaded automatically.
first, install a @magic-library
npm install --save --save-exact @magic-libraries/isthen, in javascript
export const View = props => div(['value is ',lib.is(props.value, 'string') ? '' : 'not',' a string'])
key => value store using localstorage
docs / demothe @magic-libraries/gql module encodes template strings to graphql queries ready to be sent to a server.
docs / demothe @magic-libraries/is module unifies the javascript type testing apis.
docs / demothe @magic-libraries/json module parses and stringifies json. it also returns errors instead of throwing them.
docs / demothe @magic-libraries/prevent-default module invokes event.preventDefault as a hyperapp effect.
docs / demothe @magic-libraries/uri module encodes objects to uri strings and decodes uri strings to objects.
docs / demo@magic
@magic/core libraries allow you to include client side functionality in your app.
what would javascript be without the millions of dependencies +that you can easily install and make the average webpage +slow[ly] (pun intended) grow towards a footprint of 5 megabytes.
we think that all of that bloat is unneeded, unheeded and, frankly, not optimal.
magic has one external client side dependency, +hyperapp, +[~500 lines that provide our ui state machine]. thats it. and it won't change.
we also have the tendency to write libraries specialized for our usecase, see +@magic/css, +@magic/test, +@magic/cli +and others.
once there is a lib key in at least one component, +window.lib (browser) and global.lib (nodejs) will be set, +aliasing lib as a global variable in both environments
if you need libraries in multiple otherwise independent modules, +it might be easier to keep your library dependencies in a central place.
to achieve this, one can simply create /assets/lib.mjs and export an object from it. +this object will get merged into the globalThis.lib object, +making it available as "lib" throughout your app.
export default { name: () => {} }will turn into
lib.name = () => {}all @magic-libraries and all npm packages starting with magic-library-* will be loaded automatically
first, install a @magic-library
npm install --save --save-exact @magic-libraries/isthen, in javascript
export const View = props => div(['value is ',lib.is(props.value, 'string') ? '' : 'not',' a string',])
key => value store using localstorage
the @magic-libraries/gql module encodes template strings to graphql queries ready to be sent to a server.
http request effect for @magic.
the @magic-libraries/is module unifies the javascript type testing apis.
the @magic-libraries/json module parses and stringifies json. it also returns errors instead of throwing them.
the @magic-libraries/prevent-default module invokes event.preventDefault as a hyperapp effect.
makes strings url safe. opinionated.
the @magic-libraries/try-catch module returns errors instead of throwing them.
the @magic-libraries/uri module encodes objects to uri strings and decodes uri strings to objects.
@magicthis is Mod. it gets loaded from /assets/modules/Mod.mjs
the state of this module: {"test":"testing"}
Mod.Component, a second component in /assets/modules/Mod.mjs
export const View = state =>div({ class: 'Mod' }, [h3('Mod.Mod'),p(['this is Mod. it gets loaded from ',Link({ to: 'https://github.com/magic/core/blob/master/example/assets/modules/Mod.mjs' }, '/assets/modules/Mod.mjs'),' automatically, no need to import it.',]),p(['the state of this module: ', JSON.stringify(state.module)]),])export const state = {module: {test: 'testing',},}export const style = {margin: '0 0 1em',padding: '0.5em',border: '1px solid',borderColor: 'green',h3: {margin: 0,},}export const global = {state: {module: true,},}export const Component = props => {props = typeof props === 'string' ? { header: props } : propsCHECK_PROPS(props, propTypes, 'ModComponent')const header = props.header || props.titlereturn div({ class: 'ModComponent' }, [header && h5(header),p(['Mod.Component, a second component in ',Link({ to: 'https://github.com/magic/core/example/assets/module.mjs' }, '/assets/module.mjs'),]),])}Component.style = {border: '1px solid orange',}export const propTypes = {ModComponent: [{ key: 'header', type: ['string', 'array'], required: ['title'] }],}