{"id":30181003,"url":"https://github.com/streamich/portable-example","last_synced_at":"2025-10-06T06:04:12.593Z","repository":{"id":33959433,"uuid":"37690547","full_name":"streamich/portable-example","owner":"streamich","description":"portable.js web app tutorial","archived":false,"fork":false,"pushed_at":"2023-12-15T14:56:21.000Z","size":9,"stargazers_count":2,"open_issues_count":1,"forks_count":0,"subscribers_count":2,"default_branch":"master","last_synced_at":"2024-05-01T12:32:42.378Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/streamich.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null}},"created_at":"2015-06-18T23:14:01.000Z","updated_at":"2018-06-20T09:05:50.000Z","dependencies_parsed_at":"2024-02-05T10:16:04.362Z","dependency_job_id":null,"html_url":"https://github.com/streamich/portable-example","commit_stats":null,"previous_names":[],"tags_count":0,"template":false,"template_full_name":null,"purl":"pkg:github/streamich/portable-example","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/streamich%2Fportable-example","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/streamich%2Fportable-example/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/streamich%2Fportable-example/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/streamich%2Fportable-example/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/streamich","download_url":"https://codeload.github.com/streamich/portable-example/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/streamich%2Fportable-example/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":270024697,"owners_count":24514054,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-12T02:00:09.011Z","response_time":80,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-12T08:06:27.825Z","updated_at":"2025-10-06T06:04:12.495Z","avatar_url":"https://github.com/streamich.png","language":"JavaScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# `portable.js` Introduction\n\nFirst install `portable.js` globally using `npm`.\n\n    npm i -g portable-js\n    \nThis will install a command line tool `portable.js` (or `pjs` for convenience) you will use to package your apps.\n\nTo begin, in your project's folder you will have to create a `portable.js` manifest file that describes how to bundle your files.\n \nLet's say you have a project `my-app`, with a typical node.js folder structure:\n\n    my-app/\n        app/\n            standard/\n                index.js\n            feature/\n                print-time.js\n        node_modules/\n            ...\n        package.json\n        portable.js\n        \nIn `portable.js` manifest you have to define 3 variables: `dest`, `layer`, and `bundle`.\n\n - `dest` is simply the destination folder where the output will be saved.\n - `layer` defines the 'layers' or files that you want to package together.\n - `bundle` specifies the final output target, be it a browser app or a node.js app.\n \nLet's start by creating a layer with the core functionality of our app. Edit your `portable.js` file to as follows:\n\n```javascript\nmodule.exports = {\n\n    // The output folder.\n    dest: './dist',\n    \n    // A collection of layers to build.\n    layer: {\n    \n        // A layer named \"standard\".\n        standard: {\n            // The root folder where to start to look for files.\n            src: './',\n            // An array or a string of globs to match files,\n            // which will be included in the layer.\n            glob: [\n                'app/standard/**/*.js',\n                'node_modules/**/*.+(js|json)',\n            ]\n        }\n    }\n};\n```\n\nIn your project's folder run this command to create your layers.\n\n    portable.js layer\n    \nIn `./dist` folder you will see a `.json` file containing the files you included in your layer. Now lets use that layer\nto create a web app, add to your `portable.js` a bundle definition:\n\n```javascript\nmodule.exports = {\n    dest: './dist',\n    layer: {\n        standard: {\n            src: './',\n            glob: [\n                'app/standard/**/*.js',\n                'node_modules/**/*.+(js|json)',\n            ]\n        }\n    },\n    \n    // A collection of bundles to create.\n    bundle: {\n        // A bundle named \"app\".\n        app: {\n            // Build an app for browser with minimal overhead.\n            target: 'browser-micro',\n            // Mount the \"standard\" layer you created at the \"/my-app\" folder.\n            volumes: [\n                ['/my-app', 'standard']\n            ],\n            // Provide parameters to the bundling function.\n            props: {\n                // Specify which file to run first when your app is loaded.\n                argv: ['/my-app/app/standard/index.js']\n            }\n        }    \n    }\n};\n```\n\nNow run `portable.js bundle` command and you are *done*. Your freshly baked app is now available at [`./dist/app.js`](./dist/app.js).\nAll you need to do is include it in your HTML page with `\u003cscript src=\"./dist/app.js\"\u003e\u003c/script\u003e`.\n\nTo make your workflow more efficient, you can use the `server` command which will watch your folders and rebuild the\nbundles automatically as you modify your files and instead of rewriting the bundle files in the `./dist` folder your\nbundles will be served by a HTTP server. Specify a port you want to use for the server in the manifest file:\n\n```javascript\nmodule.exports = {\n    dest: './dist',\n    layer: {\n        standard: {\n            src: './',\n            glob: [\n                'app/standard/**/*.js',\n                'node_modules/**/*.+(js|json)',\n            ]\n        }\n    },\n    bundle: {\n        app: {\n            target: 'browser-micro',\n            volumes: [\n                ['/my-app', 'standard']\n            ],\n            props: {\n                argv: ['/my-app/app/standard/index.js']\n            }\n        }    \n    },\n    \n    // Server options.\n    server: {\n        // Port the server will bind to.\n        port: 1234\n    }\n};\n```\n\nStart the server with the `server` command:\n\n    portable.js server\n    \nNow in your HTML file specify the URL of the bundle as `\u003cscript src=\"https://127.0.0.1/bundles/app.js\"\u003e\u003c/script\u003e`.\n\n*portable.js* creates an in-memory file system out of your layers. For example, you can print the location of your\ncurrent file, just as you do in node.js:\n\n```javascript\nconsole.log(__dirname);\n// /my-app/app/standard\nconsole.log(__filename);\n// /my-app/app/standard/index.js\n```\n\nThe `fs` module has two functions `fs.mountSync` and `fs.mount` that allow you to mount more files to your in-memory\nfile system.\n\n - `fs.mountSync(mountpoint: string, layer: {[s: string]: string})` where `mountpoint` is the root location of a layer\n that will be mounted, and `layer` is a JSON dictionary of relative file paths to file contents.\n - `fs.mount(mountpoint: string, url: string, callback: () =\u003e void)` is similar to `fs.mountSync` but instead you specify an URL\n of a `.json` file (a layer) which will be mounted, the callback parameter is a function that will be called when the\n layer has been downloaded and mounted.\n\nFor example, you can do:\n\n```javascript\nvar fs = require('fs');\nfs.mountSync('/my-app/app', {\n    \"hello-world.js\": 'console.log(\"Hello world!\");'\n});\nrequire('../hello-world.js');\n// Hello world!\n```\n    \nLet's say we have a feature in our app that we don't want to include in our main bundle, but we want to download that\ncode on-demand. For that we create a new layer and use `fs.mount` function to mount that layer when needed.\n\nIn your `portable.js` manifest you will create a new layer:\n\n```javascript\nmodule.exports = {\n    // ...\n    layer: {\n        // ...\n        \n        // A layer named \"feature\".\n        feature: {\n            src: './',\n            glob: 'app/feature/**/*.js'\n        }\n    },\n    \n    // ...\n};\n```\n\nNow we can load that layer when needed like so:\n\n```javascript\nvar fs = require('fs');\nfs.mount('/my-app', 'https://127.0.0.1:1234/layers/feature.json', function() {\n    require('../feature/print-time');\n});\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstreamich%2Fportable-example","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fstreamich%2Fportable-example","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fstreamich%2Fportable-example/lists"}