4.11.0 • Published 2 months ago

@squared-functions/document v4.11.0

Weekly downloads
287
License
MIT
Repository
github
Last release
2 months ago

squared-functions 0.11

These are the available options when creating archives or copying files. Examples use squared 2.3 although the concepts can be used similarly with any NodeJS application.

Image

Image conversion can be achieved using the "commands" array property in a FileAsset object. The supported formats are:

  • png - r/w
  • jpeg - r/w
  • webp - r/w
  • bmp - r/w
  • gif - r
  • tiff - r

NOTE: WebP support requires manual NPM installation of the binaries.

  • dwebp - r
  • cwebp - w

npm i dwebp-bin && npm i cwebp-bin

// All commands are optional except "format". Outer groupings and inner brackets are required.

+ <format>

- @|%
- ~size(n)(w|x) // chrome only
- ( minSize(n,0) , maxSize(n,*) )
- ( width(n|auto) x height(n|auto) [bilinear|bicubic|hermite|bezier]? ^(cover|contain|scale)?[left|center|right|top|middle|bottom]? #background-color? )
- ( left(+|-n) , top(+|-n) | cropWidth(n) x cropHeight(n) )
- { ...rotate(n) #background-color? }
- | opacity(0.0-1.0) OR jpeg_webp_quality(0-100)[photo|picture|drawing|icon|text]?[0-100]?| // cwebp: -preset -near_lossless
- !method // no arguments (e.g. jimp: dither565|greyscale|invert|normalize|opaque|sepia)

@ - replace
% - smaller

Placing an @ symbol (png@) after the format will remove the original file from the package. Using the % symbol (png%) instead will choose the smaller of the two files. You can also use these commands with the setting "convertImages" in the Android framework.

// Multiple transformations use the "::" as the separator with "data-chrome-commands"

webp(50000,*)(800x600[bezier]^contain[right|bottom]#FFFFFF)(-50,50|200x200){45,135,215,315#FFFFFF}|0.5||100[photo][75]|!opaque!greyscale

webp~800w(800x600) // chrome srcset attribute
webp~2x(1024x768)

Tinify is used for image compression and supports PNG and JPEG. The first 500 images are free each month with a developer API key.

// squared.settings.json
{
  "compress": {
    "tinify_api_key": "**********" // default api key
  }
}

// HTML configuration (json/yaml)
{
  "selector": ".card:nth-of-type(1) img",
  "type": "image",
  "compress": [
    {
      "format": "png", // OR: jpeg
      "plugin": "tinify", // optional (pre-installed)
      "options": {
        "apiKey": "**********" // optional (overrides settings)
      }
    }
  ]
}

Other formats can be compressed similarly using imagemin. Manual installation is required (plugin only) and can be configured using the options attribute.

{
  "selector": ".card:nth-of-type(1) img",
  "type": "image",
  "compress": [
    {
      "format": "png",
      "plugin": "imagemin-pngquant", // npm i imagemin-pngquant
      "options": {
        "quality": [
          0.6,
          0.8
        ]
      }
    }
  ]
}

Gulp

Tasks can be performed with Gulp to take advantage of their pre-built plugin repository. Gulp is the final stage preceding archiving or copying after file content has been downloaded and transformed.

// squared.settings.json

{
  "gulp": {
    "minify": "./gulpfile.js"
    "beautify": "./gulpfile.js",
    "compress": "./gulpfile.android.js"
  }
}

// chrome
{
  "selector": "head > script:nth-of-type(1)",
  "type": "js",
  "tasks": [
    "minify",
    "beautify"
  ]
}

// android
const options = {
    assets: [
        {
            pathname: 'images',
            filename: 'pencil.png',
            mimeType: 'image/png',
            commands: ['jpeg', 'bmp@(50000,100000)'],
            tasks: ['compress'],
            uri: 'http://localhost:3000/common/images/pencil.png'
        }
    ]
};
<!-- chrome -->
<script src="/common/system.js" data-chrome-tasks="minify+beautify"></script>

<!-- android -->
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/harbour1.jpg" data-android-tasks="compress" />

NOTE: SRC (temp) and DEST (original) always read and write to the current directory.

// gulpfile.js

const gulp = require('gulp');
const uglify = require('gulp-uglify');
 
gulp.task('minify', () => {
  return gulp.src('*')
    .pipe(uglify())
    .pipe(gulp.dest('./'));
});

Renaming files with Gulp is not recommended. It is better to use the "saveAs" or "filename" attributes when the asset is part of the HTML page.

CHROME: Saving web page assets

Bundling options are available with these HTML tag names.

  • saveAs: html + script + link
  • exportAs: script + style
  • exclude: script + link + style

Files with the same path and filename will automatically create a bundle assuming there are no conflicts in call ordering.

// HTML configuration (json/yaml) is recommended

{
  "selector": "head > script:nth-of-type(2), head > script:nth-of-type(3)",
  "type": "js",
  "saveAs": "js/modules2.js"
}

JS and CSS files can be bundled together with the "saveAs" or "exportAs" action. Multiple transformations per asset can be chained using the "+" symbol. Whitespace can be used between anything for readability.

+ saveAs: location | ~  // same
+ exportAs: location

- ::
- format (chain "+")

These are the available option modifiers:

* preserve
    - css: Prevent unused styles from being deleted
* inline
    - js: Rendered inline with <script>
    - css: Rendered inline with <style>
    - image: Rendered inline with base64 encoding as data url
* compress
    - png: TinyPNG service for PNG or JPEG
    - gz: Gzip
    - br: Brotli

NOTE: Placing the "chrome" dataset commands at the end is recommended especially if you are using the ">" character in your attributes. When possible use the "&gt;" entity instead.

<!-- Required: Lowercase tag names and attributes with no extra spaces -->

<link rel="stylesheet" href="css/dev.css" data-chrome-file="saveAs:css/prod.css::beautify" data-chrome-options="preserve|inline" />
<style data-chrome-file="exportAs:css/prod.css::minify+beautify" data-chrome-options="compress[gz]">
    body {
        font: 1em/1.4 Helvetica, Arial, sans-serif;
        background-color: #fafafa;
    }
</style>
<script src="/dist/squared.js" data-chrome-file="saveAs:js/bundle1.js::minify"></script>
<script src="/dist/squared.base.js" data-chrome-file="saveAs:js/bundle1.js::minify"></script>
<script src="/dist/chrome.framework.js" data-chrome-file="saveAs:js/bundle2.js"></script>

Bundling with "exportAs" gives you the ability to debug source code inside <script> elements.

Raw assets

<!-- img | video | audio | source | track | object | embed | iframe -->

<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/harbour1.jpg"
     data-chrome-file="saveAs:images/harbour.jpg"
     data-chrome-options="compress" />

You can use images commands with saveTo (directory) on any element where the image is the primary display output.

<!-- img | object | embed | iframe -->

<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/harbour1.jpg"
     data-chrome-file="saveTo:../images/harbour"
     data-chrome-commands="png(10000,75000)(800x600[bezier]^contain[right|bottom])"
     data-chrome-options="compress|inline" />

Transformations including the original file are given a UUID filename. Leaving "file" empty will save the transformations to the current image directory.

Built-In plugins

JS and CSS files can be optimized further using these settings:

  • beautify
  • minify
  • es5 (Babel)
  • es5-minify (UglifyJS)
  • custom name

You can also define your own optimizations in squared.settings.json:

These particular plugins can be configured using a plain object literal. Manual installation is required when using any of these packages npm run install-chrome. Transpiling with Babel is also configurable with a .babelrc file in the base folder for any presets and additional settings. Other non-builtin minifiers can similarly be applied and chained by defining a custom string-based synchronous function.

Custom plugins can be installed from NPM or copied into your local workspace. Examples can be found in the "chrome/packages" folder.

{workspace}/node_modules/@squared-functions/chrome/packages
  • Function object
  • file relative to serve.js
  • function closure
// squared.settings.json: chrome -> html | js | css -> npm package name -> process name

{
  "chrome": {
    "html": { // built-in minifier
      "posthtml": {
        "transform": {
          "plugins": [
            ["posthtml-doctype", { "doctype": "HTML 5" }], // Plugins have be installed with NPM manually
            ["posthtml-include", { "root": "./", "encoding": "utf-8" }]
          ]
        },
        "transform-output": {
          "directives": [
            { "name": "?php", "start": "<", "end": ">" }
          ]
        }
      },
      "prettier": {
        "beautify": {
          "parser": "html",
          "printWidth": 120,
          "tabWidth": 4
        }
      }
    },
    "js": { // custom function (chrome -> eval_function: true)
      "terser": {
        "minify-example": "function(context, value, output) { return context.minify(value, output).code; }", // "minify-example-output" creates scoped variable "output"
        "minify-example-output": {
          "keep_classnames": true
        }
      },
      "@babel/core": {
        "es5-example": "./es5.js" // startsWith('./ | ../')
      },
      "rollup": {
        "bundle-es6": {
          "plugins": [
            ["@rollup/plugin-json", { compact: true }]
          ],
          "external": ["lodash"]
        },
        "bundle-es6-output": "./rollup.output.config.json" // supplemental JSON configuration settings use the "-output" suffix
      },
      "npm-custom-plugin": {
        "custom-example": {
          "sourceMap": true
        }
      }
    },
    "css": {
      "postcss": {
        "transform": {
          "plugins": ["autoprefixer", "cssnano"] // Plugins have be installed with NPM manually
        }
      },
      "node-sass": { // npm i node-sass
        "sass-example": "function(context, value) { return context.renderSync({ data: value }, functions: {}); }" // first transpiler in chain
      }
    }
  }
}
// es5.js

function (context, value, output /* optional: "@babel/core-output" */) {
    const options = { presets: ['@babel/preset-env'] }; // <https://babeljs.io/docs/en/options>
    return context.transformSync(value, output).code;
}

The same concept can be used inline anywhere using a <script> tag with the type attribute set to "text/template". The script template will be completely removed from the final output.

// "es5-example" is a custom name (chrome -> eval_template: true)

<script type="text/template" data-chrome-template="js::@babel/core::es5-example">
function (context, value, output /* optional */, input /* optional */) {
    const options = { ...output, presets: ['@babel/preset-env'], sourceMaps: true };
    const result = context.transformSync(value, options);
    if (result) {
        if (result.map) {
            input.nextMap('babel', result.map, result.code);
        }
        return result.code;
    }
}
</script>

Here is the equivalent configuration in YAML and when available has higher precedence than JSON.

Modifying content attributes

There are possible scenarios when a transformation may cause an asset type to change into another format.

<!-- before -->
<link id="sass-example" rel="alternate" type="text/plain" href="css/dev.sass" />

Using element "id" is recommended when there are multiple elements with identical structure and content. Similar to JSON use only double quotes (or &quot;) and do not use unnecessary extra spaces. Tags that are not well-formed may fail to be replaced.

{
  "selector": "#sass-example",
  "type": "css",
  "filename": "prod.css",
  "attributes": {
      "id": undefined,
      "rel": "stylesheet",
      "type": "text/css",
      "title": "",
      "disabled": null
    }
  ],
  "process": [
    "node-sass"
  ]
}
<!-- after -->
<link rel="stylesheet" type="text/css" title="" disabled href="css/prod.css" />

External configuration

JSON (json/js) configuration is optional and is provided for those who prefer to separate the bundling and transformations from the HTML. Any assets inside the configuration file will override any settings either inline or from JavaScript. You can also use the equivalent in YAML (yml/yaml) for configuring as well.

interface OutputModifiers {
    inline?: boolean; // type: js | css | image (base64)
    preserve?: boolean; // type: css
    ignore?: boolean;
    exclude?: boolean
}

interface AssetCommand extends OutputModifiers {
    selector?: string;
    type?: string;
    saveAs?: string; // type: js | css
    exportAs?: string; // type: js | css
    saveTo?: string; // type: image | video | audio (transforms create multiple files and are given a UUID filename)
    pathname?: string; // alias for "saveTo"
    filename?: string; // type: html | ...image
    process?: string[]; // type: js | css
    commands?: string[]; // type: image
    cloudStorage?: CloudService[];
    attributes?: { [key: string]: value?: Null<string> };
    tasks?: string[];
    watch?: boolean | { interval?: number, expires?: string }; // type: js | css | image (expires: 1h 1m 1s)
    template?: {
        module: string;
        identifier?: string;
        value?: string;
    };
}
squared.saveAs('bundle.zip', { configUri: 'http://localhost:3000/chrome/bundle.yml' });

Here is the equivalent page using only inline commands with "data-chrome-file" and "data-chrome-tasks".

Cloud storage

Manual installation of the SDK is required including an account with at least one of these cloud storage provider.

* Amazon
  - npm i aws-sdk
  - AWS: https://aws.amazon.com/free (5GB - 12 months)

* Microsoft
  - npm i @azure/storage-blob
  - Azure: https://azure.microsoft.com/en-us/free (5GB - 12 months)

* Google
  - npm i @google-cloud/storage
  - GCloud: https://cloud.google.com/free (5GB - US)

* IBM
  - npm i ibm-cos-sdk
  - IBM: https://www.ibm.com/cloud/free (25GB)

* Oracle
  - npm i aws-sdk
  - OCI: https://www.oracle.com/cloud/free (10GB)
  - Uses S3 compatibility API
  - Cannot create new public buckets

Other service providers can be integrated similarly except for credential verification.

// Optional fields are supported by all services

{
  "selector": "#picture1",
  "type": "image",
  "commands": [
    "png(100x200){90,180,270}" // Uploaded with UUID filename
  ],
  "cloudStorage": [
    {
      "service": "aws",
      "bucket": "squared-001",
      "credential": {
        "accessKeyId": "**********",
        "secretAccessKey": "**********",
        "region": "us-west-2", // Custom properties are sent to the S3 client (optional)
        "sessionToken": "**********" // optional
      },
      "credential": "main", // OR: Load host configuration from settings at instantiation
      "upload": {
        "active": false, // Rewrites "src" to cloud storage location (optional)
        "localStorage": false, // Remove current file from archive or local disk (optional)
        "filename": "picture1.webp", // Choose a different bucket filename (optional)
        "all": false, // Include transforms (optional)
        "overwrite": false // Always use current filename (optional)
      },
      "download": {
        "filename": "picture2.png",
        "versionId": "12345", // Retrieve a previous file snapshot (optional)
        "pathname": "download/images", // File adjacent or base directory when omitted (optional: Overrides "preservePath")
        "active": false, // Always write file or rename to main file when same extension (optional)
        "overwrite": false, // Always write file (optional)
        "deleteObject": false // Remove if download success (optional)
      }
    },
    {
      "service": "azure",
      "bucket": "squared-002",
      "credential": {
        "accountName": "**********", // +1 password option (required)
        "accountKey": "**********",
        "connectionString": "**********",
        "sharedAccessSignature": "**********"
      },
      "upload": {
        "pathname": "a/b/c/", // Virtual directory in bucket (optional: Overrides "preservePath")
        "endpoint": "http://squaredjs.azureedge.net/squared-002" // e.g. CDN (optional)
      }
    },
    {
      "service": "gcloud",
      "bucket": "squared-003", // UUID generated when omitted (optional)
      "credential": {
        "keyFilename": "./gcloud.json" // Path to JSON credentials
      },
      "admin": {
        "publicRead": false, // New buckets (optional: Not supported OCI)
        "emptyBucket": false, // More convenient than using "overwrite" (optional),
        "preservePath": false // Use current pathname as file prefix
      },
      "upload": {
        "active": false, // Implicity "publicRead: true" except when explicitly "publicRead: false"
        "publicRead": false // User with "admin" privileges (optional: Not supported Azure and OCI)
      }
    },
    {
      "service": "ibm",
      "bucket": "squared-004",
      "credential": {
        "apiKeyId": "**********",
        "serviceInstanceId": "**********",
        "region": "us-south",
        "endpoint": "https://s3.us-south.cloud-object-storage.appdomain.cloud" // Same as region (optional)
      }
    },
    {
      "service": "oci",
      "bucket": "squared-005", // New buckets are private when using S3 API
      "credential": {
        "region": "us-phoenix-1",
        "namespace": "abcdefghijkl",
        "accessKeyId": "**********",
        "secretAccessKey": "**********"
      }
    }
  ]
}

NOTE: Using S3 and OCI at the same time with identical bucket names causes a conflict with the S3 region cache.

Serving CSS files from cloud storage or CDN requires every image inside the file to be hosted with an absolute URL.

squared.saveAs('index.zip', {
    configUri: 'http://localhost:3000/chrome/bundle.yml',
    saveAs: {
        html: {
            cloudStorage: [{ // Create static website
                service: 'aws',
                bucket: 'squared-001',
                settings: 'main',
                upload: {
                    active: true,
                    endpoint: 'https://squared-001.s3.us-west-2.amazonaws.com',
                    overwrite: true
                }
            }]
        },
        image: { // Non-element images using url() method
            cloudStorage: [{
                service: 'aws',
                bucket: 'squared-001',
                settings: 'main',
                upload: {
                    active: true
                }
            }]
        }
    }
});

Inline commands are not supported when using cloud features.

Cloud database

Basic text replacement can be achieved using any of these cloud based document databases. Each provider has a different query syntax and consulting their documentation is recommended.

* Amazon DynamoDB
  - npm i aws-sdk
  - AWS: https://aws.amazon.com/dynamodb (25GB + 25 RCU/WCU)

* Microsoft Cosmos DB
  - npm i @azure/cosmos
  - Azure: https://azure.microsoft.com/en-us/services/cosmos-db (5GB + 400RU/s)

* Google Firestore / BigQuery
  - npm i @google-cloud/firestore
  - npm i @google-cloud/bigquery
  - GCloud: https://cloud.google.com/firestore (1GB + 50K/20K r/w@day)
            https://cloud.google.com/bigquery (10GB + 1TB queries/month)

* IBM Cloudant
  - npm i @cloudant/cloudant
  - IBM: https://www.ibm.com/cloud/cloudant (1GB + 20/10 r/w@sec)

* Oracle Autonomous DB
  - npm i oracledb
  - OCI: https://www.oracle.com/autonomous-database (20GB)
         https://www.oracle.com/autonomous-database/autonomous-json-database (Paid - 1TB)
// NOTE: Attribute "table" is required except when using BigQuery

interface CloudDatabase {
    table: string;
    value: string | ObjectMap<string | string[]>;
    name?: string;
    id?: string;
    query?: string | PlainObject | any[];
    limit?: number;
    params?: unknown[];
    options?: PlainObject;
}

/* AWS: https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/GettingStarted.NodeJs.html */
{
  "selector": ".card:nth-of-type(1) p",
  "type": "text",
  "cloudDatabase": {
    "service": "aws",
    "credential": {
      "accessKeyId": "**********",
      "secretAccessKey": "**********",
      "region": "us-east-1", // Endpoint specified (optional)
      "endpoint": "https://dynamodb.us-east-1.amazonaws.com" // Local development (required) 
    },
    "table": "demo",
    "query": {
      "KeyConditionExpression": "#name = :value",
      "ExpressionAttributeNames": { "#name": "id" },
      "ExpressionAttributeValues": { ":value": "1" }
    },
    "limit": 1, // optional
    "value": "<b>${title}</b>: ${description}" // Only one field per template literal
  }
}

/* Azure: https://docs.microsoft.com/en-us/azure/cosmos-db/sql-query-getting-started */
{
  "selector": ".card:nth-of-type(1) p",
  "type": "text",
  "cloudDatabase": {
    "service": "azure",
    "credential": {
      "endpoint": "https://squared-001.documents.azure.com:443",
      "key": "**********"
    },
    "name": "squared", // Database name (required)
    "table": "demo",
    "partitionKey": "Pictures", // optional
    "query": "SELECT * FROM c WHERE c.id = '1'", // OR: storedProcedureId + partitionKey? + params?
    "value": "<b>${title}</b>: ${description}"
  }
}

/* GCloud: https://firebase.google.com/docs/firestore/query-data/queries */
{
  "selector": ".card:nth-of-type(1) p",
  "type": "text",
  "cloudDatabase": {
    "service": "gcloud",
    "credential": {
      "keyFilename": "./gcloud.json"
    },
    "table": "demo",
    "query": [["group", "==", "Firestore"], ["id", "==", "1"]], // where
    "orderBy": [["title", "asc"]], // optional
    "value": "<b>${title}</b>: ${description}"
  }
}

// BigQuery
{
  "selector": ".card:nth-of-type(1) p",
  "type": "text",
  "cloudDatabase": {
    "service": "gcloud",
    "credential": {
      "keyFilename": "./gcloud.json"
    },
    "query": "SELECT name, count FROM `demo.names_2014` WHERE gender = 'M' ORDER BY count DESC LIMIT 10",
    "limit": 5 // optional
    "value": "<b>${name}</b>: ${count}"
  }
}

/* IBM: https://github.com/cloudant/nodejs-cloudant#readme */
{
  "selector": ".card:nth-of-type(1) p",
  "type": "text",
  "cloudDatabase": {
    "service": "ibm",
    "credential": {
      "account": "**********", // IAM and legacy credentials
      "password": "**********",
      "url": "https://<account>:<password>@<account>.cloudantnosqldb.appdomain.cloud" // OR: Service credentials
    },
    "table": "demo",
    "query": { "selector": { "id": { "$eq": "1" } } },
    "value": "<b>${title}</b>: ${description}"
  }
}

/* OCI: https://docs.oracle.com/en/database/oracle/simple-oracle-document-access/adsdi/oracle-database-introduction-simple-oracle-document-access-soda.pdf */
{
  "selector": ".card:nth-of-type(1) p",
  "type": "text",
  "cloudDatabase": {
    "service": "oci",
    "credential": {
      "user": "**********",
      "password": "**********",
      "connectionString": "tcps://adb.us-phoenix-1.oraclecloud.com:1522/abcdefghijklmno_squared_high.adb.oraclecloud.com?wallet_location=/Users/Oracle/wallet"
    },
    "table": "demo",
    "query": "SELECT d.* from demo NESTED json_document COLUMNS(id, title, description) d WHERE d.id = '1'", // SQL: Column names might be UPPERCASED
    "query": { "id": { "$eq": "1" } }, // SODA
    "value": "<b>${title}</b>: ${description}"
  }
}
// Retrieval using ID is supported by all providers

{
  "selector": ".card:nth-of-type(2) img",
  "type": "attribute",
  "cloudDatabase": {
    "service": "azure",
    "credential": "db-main",
    "name": "squared", // Azure (required)
    "table": "demo",
    "id": "2", // OCI (server assigned)
    "partitionKey": "Pictures", // AWS (required) | Azure and IBM (optional)
    "value": {
      "src": "imageData.src", // Template literal syntax is not supported
      "style": [":join(; )" /* optional: " " */, "imageData.style[0]", "imageData.style[1]"]
    }
  }
}

Some queries use an optional parameters array (params) or configuration object (options) which is sent with the query when applicable. If you require this advanced usage then further instructions can be found in the database provider documentation.

When in development mode you can save read units by setting a timeout value for the DB cache.

// squared.settings.json

"cloud": {
  "cache": {
    "aws": 0, // No cache per reload
    "azure": 60, // 1 minute
    "gcloud": 86400 // 1 day
  }
}

Results are cached using the supplied credentials and queries will individually be cleared when the amount of time has expired.

Reusing configuration templates is possible using URL query parameters. Output values cannot be modified with the {{param}} syntax.

// http://localhost:3000/index.html?table=demo&id=1

{
  "service": "azure",
  "credential": "db-main",
  "name": "squared",
  "table": "{{table}}", // Params should always be quoted
  "partitionKey": "Pictures",
  "query": "SELECT * FROM c WHERE c.id = '{{id}}'",
  "value": "<b>${title}</b>: ${description}" // Not parsed
}

Options: Development / Production

The entire page can similarly be transformed as a group using the "saveAs" attribute in options. Cloud storage can be used for all assets except HTML using the same configuration as element selectors.

squared.saveAs('index.zip', {
    removeUnusedStyles: false, // Use only when you are not switching classnames with JavaScript
    productionRelease: false, // Ignore local url rewriting and load assets using absolute paths
    preserveCrossOrigin: false, // Ignore downloading a local copy of assets hosted on other domains

    // All attributes are optional except "filename" for <script> and <link>.
    saveAs: {
        html: { filename: 'index.html', format: 'beautify', attributes: [{ name: 'lang', value: 'en' }] },
        script: { pathname: '../js', filename: 'bundle.js', format: 'es5+es5-minify' },
        link: { pathname: 'css', filename: 'bundle.css', preserve: true, inline: true },
        image: { inline: true },
        base64: { commands: ['png'] }
    }
}); 
// js | css | image | video | audio

{
  "selector": "script",
  "type": "js",
  "watch": {
    "interval": 100,
    "expires": "1h 1m 1s"
  },
  "process": [
    "bundle",
    "minify"
  ],
  "cloudStorage": [
    {
      "service": "aws",
      "bucket": "squared-001",
      "credential": "main",
      "upload": {
        "active": true
      }
    }
  ]
}

squared.copyTo('/local/user/www', {
    watch: true,
    saveAs: {
        script: { pathname: '../js', format: 'es5+es5-minify', watch: true },
        link: { pathname: 'css', filename: 'bundle.css', watch: { interval: 500 } }
    }
});
<!-- chrome -->
<script src="/common/system.js" data-chrome-watch="true"></script>

<!-- android -->
<img src="images/harbour1.jpg" data-android-watch="1000::1h 30m" />

File watching is available and uses HTTP HEAD requests to determine modifications. You can also watch any file that is served using HTTP on a different server or computer. The HTML page itself or any inlined assets cannot be watched since changes to the DOM structure requires a complete browser reload.

Asset exclusion

You can exclude unnecessary processing files using the dataset attribute in <script|link|style> tags.

<script data-chrome-file="exclude" src="/dist/squared.js"></script>
<script data-chrome-file="exclude" src="/dist/squared.base.js"></script>
<script data-chrome-file="exclude" src="/dist/chrome.framework.js"></script>
<script data-chrome-file="exclude">
    squared.setFramework(chrome);
    squared.save();
</script>

You can similarly prevent an asset from being downloaded or transformed using the "ignore" command.

<iframe src="https://www.google.com/maps" data-chrome-file="ignore" />

LICENSE

MIT

4.11.0

2 months ago

4.10.5

3 months ago

4.10.4

3 months ago

4.10.3

4 months ago

4.10.2

5 months ago

4.10.1

5 months ago

4.9.20

10 months ago

4.10.0

9 months ago

4.8.45

11 months ago

4.9.19

11 months ago

4.9.18

11 months ago

4.9.8

1 year ago

4.9.9

1 year ago

3.10.0

1 year ago

4.8.41

1 year ago

4.8.40

1 year ago

4.8.43

1 year ago

4.9.11

1 year ago

4.8.42

1 year ago

4.9.10

1 year ago

4.9.13

1 year ago

4.8.44

1 year ago

4.9.12

1 year ago

4.9.15

1 year ago

4.9.14

1 year ago

4.9.17

1 year ago

4.9.16

1 year ago

4.8.34

1 year ago

4.8.35

1 year ago

4.8.38

1 year ago

4.8.39

1 year ago

4.9.7

1 year ago

4.9.6

1 year ago

4.9.5

1 year ago

3.9.3

1 year ago

3.9.27

1 year ago

4.8.32

1 year ago

4.8.33

1 year ago

4.9.4

1 year ago

4.8.31

1 year ago

4.9.3

1 year ago

4.9.0

1 year ago

4.9.2

1 year ago

4.9.1

1 year ago

3.8.14

1 year ago

3.8.15

1 year ago

3.9.2

1 year ago

3.9.1

1 year ago

3.9.0

1 year ago

4.8.90

1 year ago

4.8.92

1 year ago

4.8.91

1 year ago

4.8.94

1 year ago

4.8.93

1 year ago

4.8.96

1 year ago

4.8.95

1 year ago

4.8.50

1 year ago

4.8.85

1 year ago

4.8.87

1 year ago

4.8.86

1 year ago

4.8.89

1 year ago

4.8.88

1 year ago

4.8.76

1 year ago

4.8.75

1 year ago

4.8.78

1 year ago

4.8.77

1 year ago

4.8.79

1 year ago

4.8.25

1 year ago

4.8.27

1 year ago

4.8.26

1 year ago

4.8.29

1 year ago

4.8.28

1 year ago

4.8.30

1 year ago

4.8.36

1 year ago

4.8.37

1 year ago

3.8.12

1 year ago

3.8.13

1 year ago

3.8.10

1 year ago

3.8.11

1 year ago

4.8.9

1 year ago

4.8.8

1 year ago

4.8.5

1 year ago

4.8.4

1 year ago

4.8.7

1 year ago

4.8.6

1 year ago

4.8.1

1 year ago

4.8.0

2 years ago

4.8.3

1 year ago

4.8.2

1 year ago

4.7.0

2 years ago

4.7.5

2 years ago

4.7.2

2 years ago

4.7.1

2 years ago

4.7.4

2 years ago

4.7.3

2 years ago

3.8.0

2 years ago

4.6.0

2 years ago

3.8.9

1 year ago

3.8.4

2 years ago

3.8.3

2 years ago

3.8.2

2 years ago

3.8.1

2 years ago

3.8.8

1 year ago

3.8.7

1 year ago

3.8.6

1 year ago

3.8.5

2 years ago

4.5.0

2 years ago

4.5.2

2 years ago

4.5.1

2 years ago

4.5.7

2 years ago

4.5.4

2 years ago

4.5.3

2 years ago

4.5.6

2 years ago

4.5.5

2 years ago

4.8.21

1 year ago

4.8.20

1 year ago

4.8.23

1 year ago

4.8.22

1 year ago

4.8.24

1 year ago

4.8.10

1 year ago

4.8.12

1 year ago

4.8.11

1 year ago

4.8.14

1 year ago

4.8.13

1 year ago

4.8.16

1 year ago

4.8.15

1 year ago

4.8.18

1 year ago

4.8.17

1 year ago

4.8.19

1 year ago

3.7.5

2 years ago

3.7.4

2 years ago

3.7.3

2 years ago

3.7.2

2 years ago

3.7.7

2 years ago

3.7.6

2 years ago

4.4.1

2 years ago

4.4.0

2 years ago

4.4.3

2 years ago

4.4.2

2 years ago

4.4.5

2 years ago

4.4.4

2 years ago

4.3.4

2 years ago

4.3.3

2 years ago

4.3.9

2 years ago

4.3.6

2 years ago

4.3.5

2 years ago

4.3.8

2 years ago

4.3.7

2 years ago

4.3.13

2 years ago

4.3.12

2 years ago

4.3.11

2 years ago

4.3.10

2 years ago

4.3.14

2 years ago

4.3.2

2 years ago

4.3.1

2 years ago

4.3.0

2 years ago

4.2.3

2 years ago

4.2.2

2 years ago

4.2.4

2 years ago

4.2.1

2 years ago

4.2.0

2 years ago

3.6.13

2 years ago

3.6.12

2 years ago

3.7.1

2 years ago

3.7.0

2 years ago

3.6.11

2 years ago

4.1.4

2 years ago

4.1.3

2 years ago

4.1.2

2 years ago

4.1.1

2 years ago

4.0.5

2 years ago

4.0.4

2 years ago

4.0.7

2 years ago

4.0.6

2 years ago

4.0.3

2 years ago

3.6.9

2 years ago

3.6.10

2 years ago

4.1.0

2 years ago

4.0.1

2 years ago

4.0.2

2 years ago

3.6.8

2 years ago

2.4.1

2 years ago

2.4.0

2 years ago

2.4.2

2 years ago

3.6.2

2 years ago

3.6.1

2 years ago

3.6.0

2 years ago

4.0.0

2 years ago

3.6.6

2 years ago

3.6.5

2 years ago

3.6.4

2 years ago

3.6.3

2 years ago

3.6.7

2 years ago

3.5.3

2 years ago

3.5.2

2 years ago

3.5.1

2 years ago

3.5.0

2 years ago

3.4.0

2 years ago

3.4.4

2 years ago

3.4.3

2 years ago

3.4.2

2 years ago

3.4.1

2 years ago

3.4.6

2 years ago

3.4.5

2 years ago

3.3.1

2 years ago

3.3.0

2 years ago

3.3.3

2 years ago

3.3.2

2 years ago

3.2.0

2 years ago

2.3.6

2 years ago

3.1.3

2 years ago

3.1.2

2 years ago

3.1.1

2 years ago

3.1.0

2 years ago

3.1.5

2 years ago

3.1.4

2 years ago

3.0.8

2 years ago

2.3.8

2 years ago

2.3.7

2 years ago

2.3.9

2 years ago

1.3.4

2 years ago

3.0.7

3 years ago

3.0.6

3 years ago

2.3.5

3 years ago

3.0.4

3 years ago

3.0.3

3 years ago

3.0.2

3 years ago

3.0.1

3 years ago

3.0.5

3 years ago

1.3.3

3 years ago

2.3.4

3 years ago

2.3.3

3 years ago

2.2.3

3 years ago

2.2.2

3 years ago

3.0.0

3 years ago

1.2.14

3 years ago

1.3.2

3 years ago

1.3.1

3 years ago

1.3.0

3 years ago

2.3.0

3 years ago

2.3.2

3 years ago

2.3.1

3 years ago

2.2.1

3 years ago

2.2.0

3 years ago

1.2.13

3 years ago

1.2.12

3 years ago

2.1.3

3 years ago

1.2.11

3 years ago

2.1.2

3 years ago

2.1.1

3 years ago

2.1.0

3 years ago

2.0.9

3 years ago

1.2.10

3 years ago

2.0.10

3 years ago

1.2.9

3 years ago

2.0.8

3 years ago

2.0.7

3 years ago

2.0.6

3 years ago

2.0.5

3 years ago

1.2.8

3 years ago

2.0.4

3 years ago

1.2.7

3 years ago

2.0.3

3 years ago

2.0.2

3 years ago

2.0.1

3 years ago

2.0.0

3 years ago

1.1.9

3 years ago

1.2.6

3 years ago

1.1.8

3 years ago

1.2.5

3 years ago

1.1.7

3 years ago

1.2.4

3 years ago

1.2.3

3 years ago

1.1.6

3 years ago

1.2.2

3 years ago

1.1.5

3 years ago

1.2.1

3 years ago

1.2.0

3 years ago

1.1.4

3 years ago

1.1.3

3 years ago

1.1.2

3 years ago

1.1.1

3 years ago

1.1.0

3 years ago

1.0.7

3 years ago

0.15.3

3 years ago

1.0.6

3 years ago

0.15.2

3 years ago

1.0.5

3 years ago

0.15.1

3 years ago

0.15.0

3 years ago

1.0.4

3 years ago

1.0.2

3 years ago

1.0.1

3 years ago

1.0.3

3 years ago

1.0.0

3 years ago

0.14.4

3 years ago

0.14.3

3 years ago

0.14.2

3 years ago

0.14.1

3 years ago

0.14.0

3 years ago

0.13.3

3 years ago

0.13.2

3 years ago

0.13.0

3 years ago

0.13.1

3 years ago

0.12.0

3 years ago

0.11.0

3 years ago

0.10.1

3 years ago

0.10.0

3 years ago