4.11.0 • Published 2 months ago

@squared-functions/task v4.11.0

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

squared-functions 0.13

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 and has no features that require using Express.

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.

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

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
        ]
      }
    }
  ]
}

Tasks

Tasks can be performed preceding archiving or copying after file content has been downloaded and also transformed.

// squared.settings.json

{
  "task": {
    "gulp": {
      "handler": "@squared-functions/task/gulp",
      "settings": {
        "minify": "./gulpfile.js"
        "beautify": "./gulpfile.js",
        "compress": "./gulpfile.android.js"
      }
    }
  }
}

// chrome
{
  "selector": "head > script:nth-of-type(1)",
  "type": "js",
  "tasks": [
    { handler: "gulp", task: "minify" },
    { handler: "gulp", task: "beautify", preceding: "true" } // execute tasks before transformations
  ]
}

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

<!-- android -->
<img src="https://s3-us-west-2.amazonaws.com/s.cdpn.io/12005/harbour1.jpg" data-android-tasks="gulp: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 with Chrome. It is better to use the "saveAs" or "filename" attributes when the asset is part of the HTML page.

Document: CHROME

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 bundle can be chained using the "+" symbol.

+ 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 as base64 from file
* blob
    - image: Rendered as file from base64
* compress
    - png: TinyPNG service for PNG or JPEG
    - gz: Gzip
    - br: Brotli

NOTE: Whitespace can be used between anything for readability.

<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 function.

Custom plugins can also be installed from NPM. The function has to be named "transform" for validation purposes and can be asynchronous. The only difference is the context object is set to the Document module. Examples can be found in the "chrome/packages" folder.

  • 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, options, resolve) { resolve(context.minify(value, options.outputConfig).code); }", // "minify-example-output" creates variable "options.outputConfig"
        "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
        }
      },
      "sass": { // npm i sass
        "sass-example": "function (context, value, options, resolve) { resolve(context.renderSync({ ...options.outputConfig, data: value }, functions: {}).css); }"
      }
    }
  }
}
// es5.js

interface TransformOutput {
    file?: ExternalAsset;
    sourceFile?: string;
    sourcesRelativeTo?: string;
    sourceMap?: SourceMapInput;
    external?: PlainObject;
}

interface TransformOptions extends TransformOutput {
    baseConfig: StandardMap;
    outputConfig: StandardMap;
    sourceMap: SourceMapInput;
    writeFail: ModuleWriteFailMethod;
}

// Custom inline and template functions use Promise "resolve" callbacks

function (context, value, options, resolve) {
    context.transform(value, options.outputConfig, function(err, result) {
        if (!err && result) {
            resolve(result.code);
        }
        else {
            resolve();
        }
    });
}

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, options, resolve) {
    const options = { ...options.outputConfig, presets: ['@babel/preset-env'], sourceMaps: true }; // <https://babeljs.io/docs/en/options>
    const result = context.transformSync(value, options);
    if (result) {
        if (result.map) {
            options.sourceMap.nextMap('babel', result.code, result.map);
        }
        resolve(result.code);
    }
    else {
        resolve();
    }
}
</script>

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

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 | base64: image | font
    blob?: boolean; // type: image | font (base64)
    preserve?: boolean; // type: css | cross-origin: append/js | append/css
    ignore?: boolean;
    exclude?: boolean // type: js | css (remove from HTML)
}

interface AssetCommand extends OutputModifiers {
    selector?: string;
    type?: string; // js | css | image | append/js | append/css | database: text | attribute
    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[];
    document?: string | string[];
    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;
    };
}

Only one command per element is supported with the latter selectors taking precedence when there are conflicts. You can use a task if there are additional commands to perform.

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".

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"> <!-- Better to not use " />" self closing tag -->
// Inline commands are not supported

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

Similar to JSON it is better to use double quotes (or &quot;) and do not use unnecessary spaces around the opening and closing tags. It is also recommended to lower case every element tag name and attribute since the browser does this anyway when parsing your HTML document. Tags that are not well-formed may fail to be replaced.

<!-- after -->
<link rel="stylesheet" type="text/css" title="" disabled href="css/prod.css">

You can also use the workspace feature in squared-express to precompile text assets and using that to build the production release in one routine.

Appending external JS/CSS

You can append or prepend a sibling element (not child) that can be processed similar to a typical "script" or "link" element. Appends will fail if you remove the sibling selector element from the DOM.

<html>
<head>
    <title></title>
    <!-- Google Analytics -->
    <script>
    window.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;
    ga('create', 'UA-XXXXX-Y', 'auto');
    ga('send', 'pageview');
    </script>
    <script async src='https://www.google-analytics.com/analytics.js'></script>
    <!-- End Google Analytics -->
</head>
<body>
  <!-- HTML reserved characters "<" or ">" will be encoded as "&lt;" or "&gt;" -->
</body>
</html>
// All commands including prepend are supported in relation to the base type

[
  {
    "selector": "title",
    "type": "append/script", // all tags supported except "html"
    "textContent": "\\nwindow.ga=window.ga||function(){(ga.q=ga.q||[]).push(arguments)};ga.l=+new Date;\\nga('create', 'UA-XXXXX-Y', 'auto');\\nga('send', 'pageview');\\n" // YAML "|" operator preserves indentation (optional)
  },
  {
    "selector": "title",
    "type": "append/js", // prepend/css
    "preserve": true,
    "attributes": {
      "src": "https://www.google-analytics.com/analytics.js", // css: href (required)
      "async": null
    }
  }
]

NOTE: As of squared 2.4 the current state of the DOM is sent to the server including updates you may have made with JavaScript. This is sufficient for most regular text insertion.

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;
    document?: string | string[];
}

/* 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', {
    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

    removeUnusedClasses: false, // CSS classes that can be removed in current state
    removeUnusedSelectors: false, // CSS selectors [:first-child] that can be removed in current state (not recommend for pages with forms [:valid] and active states [:hover])
    retainUsedStyles: [/* css selectors */], // Styles that should be kept which are used later with JavaScript

    // 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 },
        font: { pathname: 'fonts', blob: true }
    }
}); 
// 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/util.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"></iframe>

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.1.9

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.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.1

3 years ago

0.13.0

3 years ago