├── CONTRIBUTING.md ├── LICENSE.txt ├── README.md ├── RELEASENOTES.md ├── SECURITY.md ├── THIRDPARTYLICENSE.txt ├── config.json ├── config └── eslintcustom.js ├── lib ├── add.js ├── addjsdoc.js ├── addpcss.js ├── addpwa.js ├── addsass.js ├── addtesting.js ├── addtypescript.js ├── addwebpack.js ├── build.js ├── buildCommon.js ├── buildCommon │ ├── compileTypescript.js │ ├── copyLocalComponent.js │ ├── generateComponentsCache.js │ ├── minifyComponent.js │ ├── optimizeComponent.js │ ├── restoreLocalComponentJson.js │ ├── stripLocalComponentJson.js │ └── webpack.js ├── buildComponent.js ├── buildWeb.js ├── clean.js ├── config.js ├── configure.js ├── constants.js ├── create.js ├── defaultconfig.js ├── hookRunner.js ├── indexHtmlInjector.js ├── injectorUtil.js ├── label.js ├── list.js ├── mainJsInjector.js ├── npmCopy.js ├── package.js ├── parser │ ├── dom-parser.js │ ├── dom.js │ └── sax.js ├── pcss.js ├── publish.js ├── remove.js ├── rjs │ └── r.js ├── rjsConfigGenerator.js ├── sass.js ├── scopes │ ├── component.js │ ├── exchange.js │ └── pack.js ├── search.js ├── serve.js ├── serve │ ├── connect.js │ └── watch.js ├── serveWeb.js ├── serveWebFileChangeHandler.js ├── strip.js ├── svg.js ├── templates │ ├── pack │ │ └── component.json │ ├── serviceWorkers │ │ ├── assets │ │ │ ├── icons │ │ │ │ ├── apple-icon-180.png │ │ │ │ ├── icon-192x192.png │ │ │ │ └── icon-512x512.png │ │ │ └── splashscreens │ │ │ │ ├── splash-1125x2436.jpg │ │ │ │ ├── splash-1242x2208.jpg │ │ │ │ ├── splash-1242x2688.jpg │ │ │ │ ├── splash-1536x2048.jpg │ │ │ │ ├── splash-1668x2224.jpg │ │ │ │ ├── splash-1668x2388.jpg │ │ │ │ ├── splash-2048x2732.jpg │ │ │ │ ├── splash-640x1136.jpg │ │ │ │ ├── splash-750x1334.jpg │ │ │ │ └── splash-828x1792.jpg │ │ ├── manifest.json │ │ ├── sw.txt │ │ ├── swInit.txt │ │ └── swinit._js │ ├── typescript │ │ └── tsconfig.json │ └── webpack │ │ └── ojet.config.js ├── util.js ├── utils.exchange.js ├── validations.js └── webpack │ ├── build.js │ ├── custom-tsc │ └── index.js │ ├── serve.js │ ├── setup.js │ ├── utils.js │ ├── webpack.common.js │ ├── webpack.development.js │ └── webpack.production.js ├── oraclejet-tooling.js ├── package.json └── test ├── config.js ├── hook.js └── util.js /CONTRIBUTING.md: -------------------------------------------------------------------------------- 1 | # Contributing to oraclejet-tooling 2 | 3 | *Copyright (c) 2014, 2025 Oracle and/or its affiliates 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/* 6 | 7 | **Pull requests are currently not being accepted for the Oracle JET project.** 8 | 9 | We plan to provide this functionality in the future. At that time, you will need to follow [The Oracle Contributor Agreement](https://www.oracle.com/technetwork/community/oca-486395.html) 10 | (OCA). 11 | 12 | If you have ideas, comments, or issues to discuss, swing on by the [Oracle JET discussion forum.](https://community.oracle.com/community/development_tools/oracle-jet/generators) 13 | 14 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Copyright (c) 2025 Oracle and/or its affiliates. 2 | 3 | The Universal Permissive License (UPL), Version 1.0 4 | 5 | Subject to the condition set forth below, permission is hereby granted to any 6 | person obtaining a copy of this software, associated documentation and/or data 7 | (collectively the "Software"), free of charge and under any and all copyright 8 | rights in the Software, and any and all patent rights owned or freely 9 | licensable by each licensor hereunder covering either (i) the unmodified 10 | Software as contributed to or provided by such licensor, or (ii) the Larger 11 | Works (as defined below), to deal in both 12 | 13 | (a) the Software, and 14 | (b) any piece of software and/or hardware listed in the lrgrwrks.txt file if 15 | one is included with the Software (each a "Larger Work" to which the Software 16 | is contributed by such licensors), 17 | 18 | without restriction, including without limitation the rights to copy, create 19 | derivative works of, display, perform, and distribute the Software and make, 20 | use, sell, offer for sale, import, export, have made, and have sold the 21 | Software and the Larger Work(s), and to sublicense the foregoing rights on 22 | either these or other terms. 23 | 24 | This license is subject to the following condition: 25 | The above copyright notice and either this complete permission notice or at 26 | a minimum a reference to the UPL must be included in all copies or 27 | substantial portions of the Software. 28 | 29 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 30 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 31 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 32 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 33 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 34 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 35 | SOFTWARE. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # @oracle/oraclejet-tooling 18.1.0 2 | 3 | ## About the tooling API 4 | This tooling API contains methods to build and serve Oracle JET web apps. It is intended to be used with task running tools such as grunt or gulp. The APIs can also be invoked directly. 5 | 6 | This is an open source project maintained by Oracle Corp. 7 | 8 | ## Installation 9 | This module will be automatically installed when you scaffold a web app following the [Oracle JET Developers Guide](http://www.oracle.com/pls/topic/lookup?ctx=jet1810&id=homepage). 10 | 11 | ## Contributing 12 | This project is not accepting external contributions at this time. For bugs or enhancement requests, please file a GitHub issue unless it’s security related. When filing a bug remember that the better written the bug is, the more likely it is to be fixed. If you think you’ve found a security vulnerability, do not raise a GitHub issue and follow the instructions in our [security policy](./SECURITY.md). 13 | 14 | ## Security 15 | 16 | Please consult the [security guide](./SECURITY.md) for our responsible security vulnerability disclosure process 17 | 18 | ## License 19 | Copyright (c) 2025 Oracle and/or its affiliates and released under the 20 | [Universal Permissive License (UPL)](https://oss.oracle.com/licenses/upl/), Version 1.0 -------------------------------------------------------------------------------- /RELEASENOTES.md: -------------------------------------------------------------------------------- 1 | ## Release Notes for oraclejet-tooling ## 2 | 3 | ### 18.1.0 4 | * Switch to chokidar from gaze 5 | 6 | ### 11.0.0 7 | * oraclejet-tooling now requires node 12.21 or later 8 | 9 | ### 5.2.0 10 | * No changes 11 | 12 | ### 5.1.0 13 | * No changes 14 | 15 | ### 5.0.0 16 | * No changes 17 | 18 | ### 4.2.0 19 | * No changes 20 | 21 | ### 4.1.0 22 | * No changes 23 | 24 | ### 4.0.0 25 | * Moved module into @oracle scope, changing the name to @oracle/oraclejet-tooling 26 | 27 | ### 3.2.0 28 | * No changes 29 | 30 | ### 3.1.0 31 | * No changes 32 | 33 | ### 3.0.0 34 | * Replaced bower with npm 35 | * SASS tasks now run in CCA directories also 36 | * Added --destination=server-only option for web apps 37 | * Removed --destination=deviceOrEmulatorName option 38 | * Added ability to cutomize serve tasks such as watching additional files 39 | * Added gap://ready to inserted CSP meta tag for iOS 10 compatibility 40 | 41 | ### 2.3.0 42 | * No changes 43 | 44 | ### 2.2.0 45 | * Allow developers to configure release paths 46 | * Provide help page for tooling tasks 47 | * Allow multiple themes to be included in a built app 48 | * Grunt serve to specific iOS emulator fails 49 | * no-build option missing from grunt serve 50 | -------------------------------------------------------------------------------- /SECURITY.md: -------------------------------------------------------------------------------- 1 | # Reporting security vulnerabilities 2 | 3 | Oracle values the independent security research community and believes that 4 | responsible disclosure of security vulnerabilities helps us ensure the security 5 | and privacy of all our users. 6 | 7 | Please do NOT raise a GitHub Issue to report a security vulnerability. If you 8 | believe you have found a security vulnerability, please submit a report to 9 | [secalert_us@oracle.com][1] preferably with a proof of concept. Please review 10 | some additional information on [how to report security vulnerabilities to Oracle][2]. 11 | We encourage people who contact Oracle Security to use email encryption using 12 | [our encryption key][3]. 13 | 14 | We ask that you do not use other channels or contact the project maintainers 15 | directly. 16 | 17 | Non-vulnerability related security issues including ideas for new or improved 18 | security features are welcome on GitHub Issues. 19 | 20 | ## Security updates, alerts and bulletins 21 | 22 | Security updates will be released on a regular cadence. Many of our projects 23 | will typically release security fixes in conjunction with the 24 | Oracle Critical Patch Update program. Additional 25 | information, including past advisories, is available on our [security alerts][4] 26 | page. 27 | 28 | ## Security-related information 29 | 30 | We will provide security related information such as a threat model, considerations 31 | for secure use, or any known security issues in our documentation. Please note 32 | that labs and sample code are intended to demonstrate a concept and may not be 33 | sufficiently hardened for production use. 34 | 35 | [1]: mailto:secalert_us@oracle.com 36 | [2]: https://www.oracle.com/corporate/security-practices/assurance/vulnerability/reporting.html 37 | [3]: https://www.oracle.com/security-alerts/encryptionkey.html 38 | [4]: https://www.oracle.com/security-alerts/ 39 | -------------------------------------------------------------------------------- /config.json: -------------------------------------------------------------------------------- 1 | {"eslintDirs": ["../oraclejet-tooling/lib", "../oraclejet-tooling/hooks"], 2 | "eslintFilter": ["!../oraclejet-tooling/dist/**", "!../oraclejet-tooling/**/parser/**", "!../oraclejet-tooling/**/rjs/**"], 3 | "fixup": ["package.json"]} -------------------------------------------------------------------------------- /config/eslintcustom.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | // 8 | // Custom eslint rules file, used to deal with linbreak-style 9 | // 10 | module.exports = { 11 | "installedESLint": true, 12 | "extends": "airbnb-base", 13 | "plugins": [ 14 | ], 15 | "rules": { 16 | "comma-dangle": "off", 17 | "no-underscore-dangle": "off", 18 | "vars-on-top": "off", 19 | "func-names": "off", 20 | "linebreak-style": ["warn", "unix"], // currently evaluating this rule in the oraclejet-tooling repos. 21 | "no-console" : "off", 22 | "strict" : "off", 23 | "import/no-unresolved" : [2, {ignore: ['oraclejet-tooling']}], // since oraclejet-tooling is defined as a url, we need it in the ignore list. 24 | "no-use-before-define" : ["error", {"functions" : false, "classes" : true}], 25 | "no-plusplus": ["off", {"allowForLoopAfterthoughts": true }] // allow ++ in loops 26 | } 27 | }; 28 | -------------------------------------------------------------------------------- /lib/add.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const component = require('./scopes/component'); 15 | const CONSTANTS = require('./constants'); 16 | const pack = require('./scopes/pack'); 17 | const util = require('./util'); 18 | 19 | /** 20 | * # Switch for 'ojet.add()' 21 | * 22 | * @public 23 | * @param {string} scope 24 | * @param {Array} parameters 25 | * @param {Object} options 26 | 27 | * @returns {Promise} 28 | */ 29 | module.exports = function (scope, parameters, options) { 30 | switch (scope) { 31 | case (CONSTANTS.API_SCOPES.COMPONENT): 32 | return component.add(parameters, options); 33 | case (CONSTANTS.API_SCOPES.PACK): 34 | return pack.add(parameters, options); 35 | default: 36 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.ADD}() 'scope' parameter.`); 37 | return false; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /lib/addjsdoc.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const util = require('./util'); 15 | const config = require('./config'); 16 | 17 | /** 18 | * # 'addJsdoc' 19 | * 20 | * @public 21 | * @param {Object} options 22 | * @returns {Promise} 23 | */ 24 | module.exports = function (options) { 25 | util.log('Installing jsdoc'); 26 | const installer = util.getInstallerCommand({ options }); 27 | const jsdocLibraries = config.data.jsdocLibraries; 28 | 29 | return util.spawn( 30 | installer.installer, 31 | [ 32 | installer.verbs.install, 33 | jsdocLibraries, 34 | installer.flags.save, 35 | installer.flags.exact 36 | ] 37 | ); 38 | }; 39 | -------------------------------------------------------------------------------- /lib/addpcss.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const util = require('./util'); 15 | const config = require('./config'); 16 | 17 | /** 18 | * # 'addPcss' 19 | * 20 | * @public 21 | * @param {Object} options 22 | * @returns {Promise} 23 | */ 24 | module.exports = function (options) { 25 | const installer = util.getInstallerCommand({ options }); 26 | util.log('Installing sass and pcss'); 27 | config.loadOraclejetConfig(); 28 | const sassVer = config.data.sassVer; 29 | 30 | return util.spawn(installer.installer, [installer.verbs.install, 31 | `sass@${sassVer}`, 32 | 'postcss-calc@6.0.1', 33 | 'autoprefixer@9.1.5', 34 | installer.flags.save]); 35 | }; 36 | -------------------------------------------------------------------------------- /lib/addpwa.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | 15 | const fs = require('fs-extra'); 16 | const path = require('path'); 17 | const util = require('./util'); 18 | const CONSTANTS = require('./constants'); 19 | /** 20 | * #addpwa 21 | * 22 | * @public 23 | * @returns {Promise} 24 | */ 25 | 26 | module.exports = function () { 27 | const appName = path.basename(process.cwd()); 28 | const appNameRegex = new RegExp('@AppName@', 'g'); 29 | const pathToApp = path.join('src'); 30 | const pathToIndexHtml = path.join(pathToApp, 'index.html'); 31 | const pathToServiceWorkerTemplates = path.join(util.getToolingPath(), 32 | CONSTANTS.PATH_TO_PWA_TEMPLATES); 33 | // 1. read index.html 34 | const indexHtmlString = fs.readFileSync( 35 | pathToIndexHtml, 36 | { encoding: 'utf-8' } 37 | ); 38 | // 2. read sw.txt, replace app name token and resources to cache 39 | // according to the app's architecture, and then write to app as js file 40 | let swJsString; 41 | if (util.isVDOMApplication()) { 42 | swJsString = fs.readFileSync( 43 | path.join(pathToServiceWorkerTemplates, 'sw.txt'), 44 | { encoding: 'utf-8' }); 45 | const vdomResourcesToCache = `[ 46 | 'index.js', 47 | 'index.html', 48 | 'bundle.js', 49 | 'manifest.json', 50 | 'components/', 51 | 'libs/', 52 | 'styles/', 53 | 'assets/' 54 | ]`; 55 | const mvvmResourcesToCache = "['index.html', 'manifest.json', 'js/', 'css/', 'assets/']"; 56 | swJsString = swJsString.replace(mvvmResourcesToCache, vdomResourcesToCache); 57 | } else { 58 | swJsString = fs.readFileSync( 59 | path.join(pathToServiceWorkerTemplates, 'sw.txt'), 60 | { encoding: 'utf-8' }); 61 | } 62 | const pathToAppSw = path.join(pathToApp, 'sw.js'); 63 | if (fs.pathExistsSync(pathToAppSw)) { 64 | fs.renameSync(pathToAppSw, path.join(pathToApp, 'sw_old.js')); 65 | } 66 | fs.outputFileSync( 67 | pathToAppSw, 68 | swJsString.replace(appNameRegex, appName) 69 | ); 70 | // 3. read manifest.json, replace app name token, write to app 71 | const manifestJsonString = fs.readFileSync( 72 | path.join(pathToServiceWorkerTemplates, 'manifest.json'), 73 | { encoding: 'utf-8' } 74 | ); 75 | const pathToAppManifest = path.join(pathToApp, 'manifest.json'); 76 | if (fs.pathExistsSync(pathToAppManifest)) { 77 | fs.renameSync(pathToAppManifest, path.join(pathToApp, 'manifest_old.json')); 78 | } 79 | fs.outputFileSync( 80 | path.join(pathToApp, 'manifest.json'), 81 | manifestJsonString.replace(appNameRegex, appName) 82 | ); 83 | // 4. copy swInit.txt and add it to end of body tag index.html, add 84 | // and other necessary to end of header tag in index.html and update 85 | const swInitString = fs.readFileSync( 86 | path.join(pathToServiceWorkerTemplates, 'swInit.txt'), 87 | { encoding: 'utf-8' } 88 | ); 89 | const additionalTagsForPWALighthouseCompliance = ` 90 | 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | 102 | 103 | 104 | `; 105 | fs.outputFileSync( 106 | pathToIndexHtml, 107 | indexHtmlString.replace( 108 | new RegExp('', 'g'), 109 | `${additionalTagsForPWALighthouseCompliance}\n\n` 110 | ).replace( 111 | new RegExp('', 'g'), 112 | `${swInitString.replace(appNameRegex, appName)}\n` 113 | ) 114 | ); 115 | 116 | // 5. Copy the assets into the /src folder: This will add the needed 117 | // icons and splashscreens, among others. 118 | fs.copySync( 119 | path.join(pathToServiceWorkerTemplates, 'assets'), 120 | path.join(pathToApp, 'assets') 121 | ); 122 | 123 | // 6. Copy over swinit.js 124 | fs.copyFileSync(path.join(pathToServiceWorkerTemplates, 'swinit._js'), 125 | path.join(pathToApp, 'swinit.js')); 126 | 127 | // 7. Ensure that the app name token is replaced by the app name: 128 | fs.outputFileSync( 129 | path.join(pathToApp, 'swinit.js'), 130 | fs.readFileSync( 131 | path.join(pathToApp, 'swinit.js'), { encoding: 'utf-8' } 132 | ).replace( 133 | appNameRegex, 134 | appName 135 | ) 136 | ); 137 | 138 | return Promise.resolve(); 139 | }; 140 | -------------------------------------------------------------------------------- /lib/addsass.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const util = require('./util'); 15 | const config = require('./config'); 16 | 17 | /** 18 | * # 'addSass' 19 | * 20 | * @public 21 | * @param {Object} options 22 | * @returns {Promise} 23 | */ 24 | module.exports = function (options) { 25 | config.loadOraclejetConfig(); 26 | const sassVer = config.data.sassVer; 27 | const installer = util.getInstallerCommand({ options }); 28 | 29 | util.log('Installing sass'); 30 | return util.spawn(installer.installer, [installer.verbs.install, `sass@${sassVer}`, installer.flags.save]); 31 | }; 32 | -------------------------------------------------------------------------------- /lib/addtesting.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const util = require('./util'); 8 | const config = require('./config'); 9 | 10 | /** 11 | * Install libraries required for the test environment 12 | * mocha for MVVM and jest for VDOM architecture. 13 | * @param {Object} options 14 | * @returns 15 | */ 16 | function installTestLibraries(options) { 17 | util.log('Installing libraries required for testing infrastructure'); 18 | const installer = util.getInstallerCommand({ options }); 19 | config.loadOraclejetConfig(); 20 | let testingLibraries; 21 | 22 | if (util.isVDOMApplication()) { 23 | testingLibraries = config.data.jestTestingLibraries; 24 | } else { 25 | testingLibraries = config.data.mochaTestingLibraries; 26 | } 27 | 28 | if (!testingLibraries) { 29 | util.log.error(`Specify libraries to install in oraclejetconfig file as needed by the testing environment\n 30 | (karma for MVVM and jest for VDOM).`); 31 | } 32 | const command = ` 33 | ${installer.installer} ${installer.verbs.install} ${testingLibraries} 34 | ${installer.flags.save} ${installer.flags.exact} 35 | `.replace(/\n/g, '').replace(/\s+/g, ' '); 36 | return util.exec(command); 37 | } 38 | 39 | module.exports = function (options) { 40 | return installTestLibraries(options) 41 | .catch(util.log.error); 42 | }; 43 | -------------------------------------------------------------------------------- /lib/addtypescript.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | 15 | const util = require('./util'); 16 | const fs = require('fs-extra'); 17 | const path = require('path'); 18 | const CONSTANTS = require('./constants'); 19 | const config = require('./config'); 20 | 21 | /** 22 | * ## Helpers 23 | */ 24 | 25 | /** 26 | * ## _installTypescript 27 | * 28 | * Install Typescript locally if environment does not 29 | * have a valid global version 30 | * 31 | * @private 32 | * @param {Object} options 33 | * @returns {Promise} 34 | */ 35 | function installTypescipt(options) { 36 | util.log('Installing Typescript'); 37 | const installer = util.getInstallerCommand({ options }); 38 | config.loadOraclejetConfig(); 39 | const typescriptLibraries = config.data.typescriptLibraries; 40 | 41 | const command = `${installer.installer} ${installer.verbs.install} ${typescriptLibraries} ${installer.flags.save} ${installer.flags.exact}`; 42 | return util.exec(command); 43 | } 44 | 45 | /** 46 | * ## injectTypescriptConfig 47 | * 48 | * Inject preset tsconfig.json file into the application 49 | * 50 | * @private 51 | * @returns {Promise} 52 | */ 53 | function injectTypescriptConfig() { 54 | return util.injectFileIntoApplication({ 55 | name: CONSTANTS.TSCONFIG, 56 | src: path.join(util.getToolingPath(), CONSTANTS.PATH_TO_TSCONFIG_TEMPLATE), 57 | dest: CONSTANTS.TSCONFIG 58 | }); 59 | } 60 | 61 | /** 62 | * ## updateTypescriptConfig 63 | * 64 | * Update the injected tsconfig.json 65 | * 66 | * @public 67 | * @returns {Promise} 68 | */ 69 | function updateTypescriptConfig() { 70 | try { 71 | // Use oraclejetconfig.json for application paths since util.getConfiguredPaths might not 72 | // be setup yet 73 | const oraclejetJetConfigJson = util.readJsonAndReturnObject(CONSTANTS.ORACLE_JET_CONFIG_JSON); 74 | const tsConfigPath = util.getPathToTsConfig(); 75 | const tsconfigJson = util.readJsonAndReturnObject(tsConfigPath); 76 | // set tsconfig "include" option to refernce typescript folder based 77 | // on values in oraclejetconfig.json 78 | tsconfigJson.include = [ 79 | util.pathJoin( 80 | '.', 81 | oraclejetJetConfigJson.paths.source.common, 82 | oraclejetJetConfigJson.paths.source.typescript, 83 | '**', 84 | '*' 85 | ) 86 | ]; 87 | fs.writeJSONSync(tsConfigPath, tsconfigJson, { spaces: 2 }); 88 | return Promise.resolve(); 89 | } catch (error) { 90 | return Promise.reject(error); 91 | } 92 | } 93 | 94 | /** 95 | * ## addTypescript 96 | * 97 | * Add Typescript support to an application 98 | * 99 | * @public 100 | * @param {Object} options 101 | * @returns {Promise} 102 | */ 103 | module.exports = function (options) { 104 | if (options.template === 'webdriver-ts') { 105 | return installTypescipt(options) 106 | .catch(util.log.error); 107 | } 108 | return installTypescipt(options) 109 | .then(injectTypescriptConfig) 110 | .then(updateTypescriptConfig) 111 | .catch(util.log.error); 112 | }; 113 | -------------------------------------------------------------------------------- /lib/addwebpack.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const path = require('path'); 8 | const CONSTANTS = require('./constants'); 9 | const util = require('./util'); 10 | const config = require('./config'); 11 | 12 | /** 13 | * ## injectOjetConfig 14 | * 15 | * Inject default ojet.config file into the application 16 | * 17 | * @private 18 | * @returns {Promise} 19 | */ 20 | function injectOJetConfig() { 21 | return util.injectFileIntoApplication({ 22 | name: CONSTANTS.OJET_CONFIG, 23 | src: path.join(util.getToolingPath(), CONSTANTS.PATH_TO_OJET_CONFIG_TEMPLATE), 24 | dest: CONSTANTS.OJET_CONFIG 25 | }); 26 | } 27 | 28 | /** 29 | * Install webpack and required loaders from NPM 30 | * @param {Object} options 31 | * @returns 32 | */ 33 | function installWebpack(options) { 34 | util.log('Installing webpack and required dependencies'); 35 | const installer = util.getInstallerCommand({ options }); 36 | config.loadOraclejetConfig(); 37 | const webpackLibraries = config.data.webpackLibraries; 38 | 39 | const command = ` 40 | ${installer.installer} ${installer.verbs.install} ${webpackLibraries} 41 | ${installer.flags.save} ${installer.flags.exact} 42 | `.replace(/\n/g, '').replace(/\s+/g, ' '); 43 | return util.exec(command); 44 | } 45 | 46 | module.exports = function (options) { 47 | return installWebpack(options) 48 | .then(injectOJetConfig) 49 | .catch(util.log.error); 50 | }; 51 | -------------------------------------------------------------------------------- /lib/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const buildWeb = require('./buildWeb'); 10 | const buildComponent = require('./buildComponent'); 11 | const buildWebpack = require('./webpack/build'); 12 | const valid = require('./validations'); 13 | const config = require('./config'); 14 | const util = require('./util'); 15 | 16 | /** 17 | * # API 18 | * ## ojet.build([platform],[options]) 19 | * @public 20 | * @param {string} [platform='web'] - Platform, defaults to 'web' 21 | * @param {Object} [options] - Options object 22 | * @param {string} [options.buildType] - buildType 'dev' or 'release' 23 | * @param {string} [options.theme] - Theme, default to alta:platform 24 | * @param {Array} [options.themes] - Themes, default to first theme in the array 25 | * @param {boolean} [options.sassCompile] - Whether to compile sass 26 | * @param {string} [options.buildConfig] - Path to build configuration file 27 | * @param {boolean} [options.buildForServe] - Whether to invoke build just for Serve 28 | * @param {string} [options.setDefaultConfig] - String path to default json file 29 | * @param {string} [options.staingPath] - Path to the staging directory 30 | * @param {object} [options.inject] - Object for inject task configuration 31 | * @param {object} [options.terser] - Object for terser task configuration 32 | * @param {object} [options.copyToRelase] - Object for copyToRelease task configuration 33 | * @param {object} [options.requireJs] - Object for requireJs task configuration 34 | * @param {object} [options.sass] - Object for sass task configuration 35 | * @returns {Promise} 36 | */ 37 | module.exports = function build(platform, options) { 38 | if (util.buildWithWebpack()) { 39 | return buildWebpack(options); 40 | } 41 | if (options.buildType === 'release' && util.bundleWithWebpack()) { 42 | // when building in release mode with webpack as the bundler, 43 | // we need to run a debug build so that webpack can bundle and optimize 44 | // debug files (and not minified files) 45 | // eslint-disable-next-line no-param-reassign 46 | options.buildType = 'debug'; 47 | // eslint-disable-next-line no-param-reassign 48 | options.release = false; 49 | // eslint-disable-next-line no-param-reassign 50 | options.bundler = 'webpack'; 51 | } 52 | // Component builds should always be targeted at platform = web 53 | const isComponentBuild = util.hasProperty(options, 'component'); 54 | const platformOption = isComponentBuild ? 'web' : platform; 55 | config.loadOraclejetConfig(platformOption); 56 | const validPlatform = valid.platform(platformOption); 57 | const validOptions = valid.buildOptions(options, validPlatform); 58 | const validBuildType = valid.buildType(validOptions); 59 | if (isComponentBuild) { 60 | valid.component(options); 61 | config.set('platform', platformOption); 62 | return buildComponent(options.component, validOptions); 63 | } 64 | config.set('platform', validPlatform); 65 | return buildWeb(validBuildType, validOptions); 66 | }; 67 | -------------------------------------------------------------------------------- /lib/buildCommon/restoreLocalComponentJson.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const util = require('../util'); 8 | const fs = require('fs-extra'); 9 | const CONSTANTS = require('../constants'); 10 | const path = require('path'); 11 | 12 | /** 13 | * Restores the stripped of component.json from staging 14 | * location after calling stripLocalComponentJson in buildWeb: 15 | */ 16 | function restoreLocalComponentJson(context) { 17 | const componentsCache = util.getComponentsCache(); 18 | util.getLocalCompositeComponents().forEach((_component) => { 19 | let pathToComponentJson; 20 | const { componentJson, builtPath } = componentsCache[_component]; 21 | // Retrieve the component.json contents from cache and write them in web: 22 | if (util.isJETPack({ pack: _component, componentJson })) { 23 | const dependenciesFromCache = [ 24 | ...Object.getOwnPropertyNames(componentJson.dependencies || {}), 25 | ...util.getMonoPackMemberNameList(componentJson) 26 | ]; 27 | dependenciesFromCache.forEach((component) => { 28 | const componentData = componentsCache[component]; 29 | if (componentData && componentData.builtPath) { 30 | pathToComponentJson = path.join(componentData.builtPath, CONSTANTS.JET_COMPONENT_JSON); 31 | fs.writeJsonSync(pathToComponentJson, componentData.componentJson); 32 | } 33 | }); 34 | } else { 35 | pathToComponentJson = path.join(builtPath, CONSTANTS.JET_COMPONENT_JSON); 36 | fs.writeJsonSync(pathToComponentJson, componentJson); 37 | } 38 | }); 39 | return Promise.resolve(context); 40 | } 41 | 42 | module.exports = restoreLocalComponentJson; 43 | -------------------------------------------------------------------------------- /lib/buildCommon/stripLocalComponentJson.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const fs = require('fs-extra'); 10 | const path = require('path'); 11 | const util = require('../util'); 12 | const CONSTANTS = require('../constants'); 13 | 14 | /** 15 | * 16 | * Stripping implementation exported to buildCommon.js: 17 | */ 18 | function stripLocalComponentJson(context) { 19 | const componentsCache = util.getComponentsCache(); 20 | util.getLocalCompositeComponents().forEach((_component) => { 21 | let pathToComponentJSON; 22 | let modifiedComponentJSON; 23 | let jsonFileContent; 24 | const { builtPath, componentJson } = componentsCache[_component]; 25 | // Modify the component.json in staging by stripping the unwanted attributes and 26 | // sub-properties during run-time. This stripped json file is then included in 27 | // the web/../min/loader.js file. The original contents of the json file are restored in 28 | // restoreLocalCcaComponentJson after minification. Also, if the passed component is a 29 | // pack, we will need to iterate through its component dependencies. Failure to do so 30 | // will result in having non-stripped metadata in the component's min/loader.js file: 31 | if (util.isJETPack({ pack: _component, componentJson })) { 32 | const dependenciesFromCache = [ 33 | ...Object.getOwnPropertyNames(componentJson.dependencies || {}), 34 | ...util.getMonoPackMemberNameList(componentJson) 35 | ]; 36 | dependenciesFromCache.forEach((component) => { 37 | const componentData = componentsCache[component]; 38 | if (componentData && componentData.builtPath) { 39 | pathToComponentJSON = path.join(componentData.builtPath, CONSTANTS.JET_COMPONENT_JSON); 40 | if (fs.readdirSync(componentData.builtPath).includes('loader.js')) { 41 | jsonFileContent = fs.readJSONSync(pathToComponentJSON); 42 | modifiedComponentJSON = getStrippedComponentJson(jsonFileContent); 43 | fs.writeJsonSync(pathToComponentJSON, modifiedComponentJSON); 44 | } 45 | } 46 | }); 47 | } else { 48 | pathToComponentJSON = path.join(builtPath, CONSTANTS.JET_COMPONENT_JSON); 49 | jsonFileContent = fs.readJSONSync(pathToComponentJSON); 50 | modifiedComponentJSON = getStrippedComponentJson(jsonFileContent); 51 | fs.writeJsonSync(pathToComponentJSON, modifiedComponentJSON); 52 | } 53 | }); 54 | return Promise.resolve(context); 55 | } 56 | 57 | /** 58 | * 59 | * @param {object} componentJSON 60 | * @returns {Object} modified componentJSON 61 | * 62 | * Strips off the attributes and their sub-properties that are not 63 | * needed during run time. The componentJSON object comes from staging 64 | * location but it is later on restored in restoreLocalCcaComponentJson: 65 | */ 66 | function getStrippedComponentJson(componentJSON) { 67 | // Top level attributes required at run-time (RT). Name and pack are not needed at RT, 68 | // but they are needed during the optimization of the pack's components: 69 | const requiredTopLevelAttributes = new Set([ 70 | 'properties', 71 | 'methods', 72 | 'events', 73 | 'slots', 74 | 'dynamicSlots', 75 | 'pack', 76 | 'name', 77 | 'version', 78 | 'dependencies', 79 | 'jetVersion', 80 | 'main' 81 | ]); 82 | // Go through the required attributes and remove the sub-properties not needed at RT: 83 | stripTopLevelAttributes(componentJSON, requiredTopLevelAttributes); 84 | return componentJSON; 85 | } 86 | 87 | /** 88 | * 89 | * @param {object} json 90 | * @param {Array} requiredAttributes 91 | * 92 | * Strips off the top level attributes that are not needed during run time. 93 | */ 94 | function deleteUnrequiredTopLevelAttributes(json, requiredAttributes) { 95 | const attributes = getAttributes(json); 96 | if (attributes) { 97 | attributes.forEach((attribute) => { 98 | if (!requiredAttributes.has(attribute)) { 99 | if (attribute === 'extension') { 100 | stripExtensionSubAttribute(json[attribute], json, attribute); 101 | } else { 102 | // eslint-disable-next-line no-param-reassign 103 | delete json[attribute]; 104 | } 105 | } 106 | }); 107 | } 108 | } 109 | 110 | /** 111 | * 112 | * @param {object} json 113 | * @returns {Array | undefined} 114 | * 115 | * Returns the names of the attributes of the passed object. 116 | */ 117 | function getAttributes(json) { 118 | if (isObject(json)) { 119 | return Object.getOwnPropertyNames(json); 120 | } 121 | return undefined; 122 | } 123 | 124 | /** 125 | * 126 | * @param {object} json 127 | * @returns {Boolean} 128 | * 129 | * Strips off the top level attributes that are not needed during run time. 130 | */ 131 | function isObject(json) { 132 | // Do not allow array objects: 133 | return json instanceof Object && !Array.isArray(json); 134 | } 135 | 136 | /** 137 | * 138 | * @param {object} componentJSON 139 | * @param {Array} requiredTopLevelAttributes 140 | * 141 | * Strips off sub-attributes of the needed top level attributes that are not needed during run time. 142 | */ 143 | function stripTopLevelAttributes(componentJSON, requiredTopLevelAttributes) { 144 | // Remove non-object top-level attributes not needed at RT: 145 | deleteUnrequiredTopLevelAttributes(componentJSON, requiredTopLevelAttributes); 146 | requiredTopLevelAttributes.forEach((topLevelAttribute) => { 147 | if (isObject(componentJSON[topLevelAttribute]) && topLevelAttribute !== 'dependencies') { 148 | const subAttributes = getAttributes(componentJSON[topLevelAttribute]); 149 | if (subAttributes) { 150 | subAttributes.forEach((subAttribute) => { 151 | stripSubAttributes(componentJSON[topLevelAttribute][subAttribute]); 152 | }); 153 | // Delete the resulting object if empty: 154 | if (isEmpty(componentJSON[topLevelAttribute])) { 155 | // eslint-disable-next-line no-param-reassign 156 | delete componentJSON[topLevelAttribute]; 157 | } 158 | } 159 | } 160 | }); 161 | } 162 | 163 | /** 164 | * 165 | * @param {object} attributeObject 166 | * 167 | * Strip the sub-attributes from the top level componentJson 168 | * properties. Go through the passed object recursively and 169 | * remove the unrequired sub-attributes. 170 | */ 171 | function stripSubAttributes(attributeObject) { 172 | const attributes = getAttributes(attributeObject); 173 | attributes.forEach((attribute) => { 174 | stripSubAttribute(attributeObject[attribute], attributeObject, attribute); 175 | }); 176 | } 177 | 178 | /** 179 | * 180 | * @param {string} attributeName 181 | * @param {object} attributeObject 182 | * @param {object} subAttributeObject -- attributeObject[attributeName] 183 | * 184 | * Strip the sub-attributes. Go through the passed object recursively and 185 | * remove the unrequired sub-attributes. 186 | */ 187 | function stripSubAttribute(subAttributeObject, attributeObject, attributeName) { 188 | if (isAttributeProtectedFromStripping(attributeName)) { 189 | return; 190 | } 191 | 192 | if (!isNeededSubAttribute(attributeName) && !isObject(subAttributeObject)) { 193 | // eslint-disable-next-line no-param-reassign 194 | delete attributeObject[attributeName]; 195 | return; 196 | } 197 | 198 | if (attributeName === 'extension') { 199 | stripExtensionSubAttribute(subAttributeObject, attributeObject, attributeName); 200 | if (isEmpty(subAttributeObject)) { 201 | // eslint-disable-next-line no-param-reassign 202 | delete attributeObject[attributeName]; 203 | } 204 | return; 205 | } 206 | 207 | const attributes = getAttributes(subAttributeObject); 208 | if (!attributes) { 209 | return; 210 | } 211 | 212 | attributes.forEach((attribute) => { 213 | stripSubAttribute(subAttributeObject[attribute], subAttributeObject, attribute); 214 | }); 215 | deleteAttribute(subAttributeObject, attributeObject, attributeName); 216 | } 217 | 218 | /** 219 | * 220 | * @param {string} subAttribute 221 | * @returns {Boolean} 222 | * 223 | * Checks if the passed attribute is a needed sub-attribute. 224 | */ 225 | function isNeededSubAttribute(subAttribute) { 226 | const requiredSubAttributes = new Set([ 227 | 'enumValues', 228 | 'properties', 229 | 'readOnly', 230 | 'type', 231 | 'value', 232 | 'binding', 233 | 'writeback', 234 | 'internalName', 235 | 'consume', 236 | 'provide', 237 | 'name', 238 | 'default', 239 | 'transform', 240 | 'extension', 241 | 'implicitBusyContext' 242 | ]); 243 | if (requiredSubAttributes.has(subAttribute)) { 244 | return true; 245 | } 246 | return false; 247 | } 248 | 249 | /** 250 | * 251 | * @param {string} subAttribute 252 | * @returns {Boolean} 253 | * 254 | * Checks if the passed attribute is a needed non-required sub-attribute. 255 | */ 256 | function isNeededExtensionSubAttribute(subAttribute) { 257 | const nonRequiredSubAttributes = new Set([ 258 | 'description', 259 | 'displayName', 260 | 'tags', 261 | 'coverImage', 262 | 'screenshots', 263 | 'jet', 264 | 'vbdt', 265 | 'audits', 266 | 'package', 267 | 'docUrl', 268 | 'itemProperties', 269 | 'webelement', 270 | 'readme', 271 | 'unsupportedBrowsers', 272 | 'businessApprovals', 273 | 'uxSpecs', 274 | 'unsupportedThemes', 275 | 'defaultColumns', 276 | 'minColumns', 277 | 'itemProperties', 278 | 'slotData', 279 | 'exceptionStatus', 280 | 'catalog', 281 | 'oracle', 282 | 'themes' 283 | ]); 284 | if (nonRequiredSubAttributes.has(subAttribute)) { 285 | return false; 286 | } 287 | return true; 288 | } 289 | 290 | /** 291 | * 292 | * @param {string} attributeName 293 | * @param {object} attributeObject 294 | * @param {object} subAttributeObject -- attributeObject[attributeName] 295 | * 296 | * Strip the extension sub-attributes. Go through the passed object recursively and 297 | * remove the unrequired sub-attributes. 298 | */ 299 | function stripExtensionSubAttribute(subAttributeObject, attributeObject, attributeName) { 300 | if (!isNeededExtensionSubAttribute(attributeName)) { 301 | // eslint-disable-next-line no-param-reassign 302 | delete attributeObject[attributeName]; 303 | return; 304 | } 305 | const attributes = getAttributes(subAttributeObject); 306 | if (!attributes) { 307 | return; 308 | } 309 | 310 | attributes.forEach((attribute) => { 311 | stripExtensionSubAttribute(subAttributeObject[attribute], subAttributeObject, attribute); 312 | }); 313 | deleteExtensionSubAttribute(subAttributeObject, attributeObject, attributeName); 314 | } 315 | 316 | /** 317 | * 318 | * @param {string} parentAttributeName 319 | * @param {object} parentAttributeObject 320 | * @param {object} childAttributeObject -- parentAttributeObject[parentAttributeName] 321 | * @returns {Boolean} 322 | * 323 | * Deletes the unrequired attribute at RT: 324 | */ 325 | function deleteAttribute(childAttributeObject, parentAttributeObject, parentAttributeName) { 326 | const attributes = getAttributes(childAttributeObject); 327 | if (attributes) { 328 | attributes.forEach((attribute) => { 329 | if (!isNeededSubAttribute(attribute) && !isObject(childAttributeObject[attribute])) { 330 | // eslint-disable-next-line no-param-reassign 331 | delete childAttributeObject[attribute]; 332 | } 333 | if (isEmpty(childAttributeObject)) { 334 | // eslint-disable-next-line no-param-reassign 335 | delete parentAttributeObject[parentAttributeName]; 336 | } 337 | }); 338 | if (isEmpty(childAttributeObject)) { 339 | // eslint-disable-next-line no-param-reassign 340 | delete parentAttributeObject[parentAttributeName]; 341 | } 342 | } 343 | } 344 | 345 | /** 346 | * 347 | * @param {string} parentAttributeName 348 | * @param {object} parentAttributeObject 349 | * @param {object} childAttributeObject -- parentAttributeObject[parentAttributeName] 350 | * @returns {Boolean} 351 | * 352 | * Deletes the unrequired attribute at RT: 353 | */ 354 | // eslint-disable-next-line max-len 355 | function deleteExtensionSubAttribute(childAttributeObject, parentAttributeObject, parentAttributeName) { 356 | const attributes = getAttributes(childAttributeObject); 357 | if (attributes) { 358 | attributes.forEach((attribute) => { 359 | if (!isNeededExtensionSubAttribute(attribute) && !isObject(childAttributeObject[attribute])) { 360 | // eslint-disable-next-line no-param-reassign 361 | delete childAttributeObject[attribute]; 362 | } 363 | if (isEmpty(childAttributeObject)) { 364 | // eslint-disable-next-line no-param-reassign 365 | delete parentAttributeObject[parentAttributeName]; 366 | } 367 | }); 368 | if (isEmpty(childAttributeObject)) { 369 | // eslint-disable-next-line no-param-reassign 370 | delete parentAttributeObject[parentAttributeName]; 371 | } 372 | } 373 | } 374 | 375 | /** 376 | * 377 | * @param {object} json 378 | * @returns {Boolean} 379 | * 380 | * Checks if the passed object is empty. 381 | */ 382 | function isEmpty(json) { 383 | if (isObject(json) && JSON.stringify(json) === '{}') { 384 | return true; 385 | } 386 | return false; 387 | } 388 | 389 | /** 390 | * 391 | * @param {object} json 392 | * @returns {Boolean} 393 | * 394 | * Checks if the passed attribute is protected from the stripping process 395 | */ 396 | function isAttributeProtectedFromStripping(attribute) { 397 | const protectedValues = [ 398 | 'value' 399 | ]; 400 | 401 | return protectedValues.includes(attribute); 402 | } 403 | 404 | module.exports = stripLocalComponentJson; 405 | -------------------------------------------------------------------------------- /lib/buildCommon/webpack.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const path = require('path'); 8 | const util = require('../util'); 9 | const hookRunner = require('../hookRunner'); 10 | const pathGenerator = require('../rjsConfigGenerator'); 11 | const CONSTANTS = require('../constants'); 12 | const ojetUtils = require('../util'); 13 | 14 | let webpack; 15 | let WebpackRequireFixupPlugin; 16 | 17 | module.exports = function (context) { 18 | // eslint-disable-next-line global-require, import/newline-after-import, import/no-dynamic-require 19 | webpack = util.requireLocalFirst('webpack'); 20 | const requireFixupPluginPath = path.join( 21 | util.getOraclejetPath(), 22 | CONSTANTS.WEBPACK_TOOLS_PATH, 23 | 'plugins/WebpackRequireFixupPlugin' 24 | ); 25 | // eslint-disable-next-line global-require, import/newline-after-import, import/no-dynamic-require 26 | WebpackRequireFixupPlugin = require(requireFixupPluginPath); 27 | // eslint-disable-next-line no-param-reassign 28 | context.opts.webpack = { 29 | config: _createConfig(context) 30 | }; 31 | return hookRunner('before_webpack', context) 32 | .then(_runWebpack); 33 | }; 34 | 35 | function _createConfig(context) { 36 | const configPaths = util.getConfiguredPaths(); 37 | const oraclejetConfig = util.getOraclejetConfigJson(); 38 | const entryFile = oraclejetConfig.architecture === CONSTANTS.VDOM_ARCHITECTURE ? 'index.js' : 'root.js'; 39 | const bundleName = util.getBundleName().full; 40 | 41 | // use polyfill for chai unless it's resolvable 42 | let chai = false; // eslint-disable-line 43 | try { 44 | chai = require.resolve('chai'); 45 | } catch (ex) {} // eslint-disable-line 46 | 47 | return { 48 | entry: path.resolve(context.opts.stagingPath, configPaths.src.javascript, entryFile), 49 | mode: 'production', 50 | output: { 51 | filename: bundleName, 52 | path: path.resolve(context.opts.stagingPath, configPaths.src.javascript) 53 | }, 54 | resolveLoader: { 55 | modules: ['node_modules', path.join(util.getOraclejetPath(), CONSTANTS.WEBPACK_TOOLS_PATH, 'loaders')], 56 | alias: { 57 | ojL10n: 'ojL10n-loader', 58 | text: 'text-loader', 59 | css: 'style-loader', 60 | ojcss: 'style-loader' 61 | } 62 | }, 63 | resolve: { 64 | alias: _createPathMappings(context), 65 | fallback: { chai } 66 | }, 67 | module: { 68 | rules: [ 69 | { 70 | test: /\.json$/, 71 | use: [], 72 | type: 'javascript/auto' 73 | }, 74 | { 75 | test: /\.css$/, 76 | use: [ 77 | 'style-loader', 78 | 'css-loader', 79 | { 80 | loader: 'css-fix-url-loader', 81 | // There is no path to images/../../redwood/images/. 82 | // Enable webpack to resolve it as images/. 83 | options: { 84 | from: './images', 85 | to: '../../../../../../images', 86 | } 87 | }, 88 | { 89 | loader: 'css-fix-url-loader', 90 | // There is no 'images' folder under the created theme's folder. 91 | // Redirect the path to the alta's images subfolder. 92 | options: { 93 | from: 'images/animated-overlay.gif', 94 | to: `../../../alta/${ojetUtils.getJETVersion()}/common/images/animated-overlay.gif`, 95 | } 96 | }, 97 | // There is no path to ../../css/redwood/images/AI-Sparkle.gif. 98 | // Configure webpack to resolve it as ./images/AI-Sparkle.gif. 99 | { 100 | loader: 'css-fix-url-loader', 101 | options: { 102 | from: '../../css/redwood/images/AI-Sparkle.gif', 103 | to: './images/AI-Sparkle.gif', 104 | } 105 | } 106 | ] 107 | } 108 | ] 109 | }, 110 | plugins: [ 111 | new webpack.LoaderOptionsPlugin({ 112 | options: { 113 | ojL10nLoader: { 114 | locale: 'en-US' 115 | } 116 | } 117 | }), 118 | new webpack.ProvidePlugin({ 119 | $: 'jquery', 120 | jQuery: 'jquery' 121 | }), 122 | new WebpackRequireFixupPlugin({ 123 | ojModuleResources: { 124 | root: path.resolve(context.opts.stagingPath, configPaths.src.javascript), 125 | view: { 126 | prefix: 'text!', 127 | match: '^\\./views/.+\\.html$' 128 | }, 129 | viewModel: { 130 | match: '^\\./viewModels/.+\\.js$', 131 | addExtension: '.js' 132 | } 133 | }, 134 | baseResourceUrl: path.join(context.opts.stagingPath, configPaths.src.javascript, 'libs/oj', `v${util.getJETVersion()}`) 135 | }) 136 | ] 137 | }; 138 | } 139 | 140 | function _createPathMappings(context) { 141 | const configPaths = util.getConfiguredPaths(); 142 | const pathMappings = {}; 143 | const defaultPathMappings = pathGenerator.getPathsMapping(context, true); 144 | Object 145 | .keys(defaultPathMappings) 146 | .forEach((key) => { 147 | pathMappings[key] = path.resolve( 148 | context.opts.stagingPath, 149 | configPaths.src.javascript, 150 | defaultPathMappings[key] 151 | ); 152 | }); 153 | // We have both single-level multi-level path mappings for preact e.g. 154 | // "preact" and "preact/hooks", "preact/debug" etc. For webpack, we need 155 | // to define the single-level "preact" mapping with the "$" suffix so that 156 | // "preact/*" imports are not resolved to "preact"; 157 | if (pathMappings.preact) { 158 | pathMappings.preact$ = pathMappings.preact; 159 | delete pathMappings.preact; 160 | } 161 | return pathMappings; 162 | } 163 | 164 | function _runWebpack(context) { 165 | return new Promise((resolve, reject) => { 166 | util.log('Running webpack'); 167 | const { config } = context.opts.webpack; 168 | webpack(config, (err, stats) => { 169 | if (err) { 170 | console.error(err.stack || err); 171 | if (err.details) { 172 | console.error(err.details); 173 | } 174 | reject(err.details); 175 | } 176 | console.log(stats.toString()); 177 | resolve(context); 178 | }); 179 | }); 180 | } 181 | -------------------------------------------------------------------------------- /lib/buildComponent.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const path = require('path'); 10 | const util = require('./util'); 11 | const hookRunner = require('./hookRunner'); 12 | const buildCommon = require('./buildCommon'); 13 | const config = require('./config'); 14 | const generateComponentsCache = require('./buildCommon/generateComponentsCache'); 15 | const constants = require('./constants'); 16 | 17 | module.exports = function buildComponent(component, opts) { 18 | // force component build to run in release mode. 19 | const context = { platform: 'web', buildType: 'release', opts }; 20 | const componentsCache = generateComponentsCache({ context }); 21 | config.set('componentsCache', componentsCache); 22 | const componentCache = componentsCache[component]; 23 | if (!componentCache) { 24 | util.log.error(`${component} is not a valid component name.`); 25 | } 26 | const { componentJson } = componentCache; 27 | context.componentConfig = componentJson; 28 | // Note that in order to ensure that the proper libs 29 | // are in web/ (platform:web buildType:release) we must call copyLibs(). 30 | return buildCommon.copyLibs(context) 31 | .then(() => buildCommon.copySingleCca(context, componentJson, component)) 32 | .then(() => buildCommon.compileComponentTypescript({ 33 | context, 34 | component, 35 | version: componentJson.version 36 | })) 37 | .then(() => buildCommon.minifyComponent(context, componentJson, component)) 38 | .then(() => hookRunner('after_component_build', context)) 39 | .then(() => { 40 | if (!componentsCache[component].isVComponent) { 41 | // Write cached component.json to staging location, for non vcomponents 42 | const { builtPath } = componentsCache[component]; 43 | const componentJsonPath = path.join(builtPath, constants.JET_COMPONENT_JSON); 44 | util.writeObjectAsJsonFile(componentJsonPath, componentJson); 45 | } 46 | }) 47 | .then(data => Promise.resolve(data)) 48 | .catch(err => Promise.reject(err)); 49 | }; 50 | -------------------------------------------------------------------------------- /lib/buildWeb.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const buildCommon = require('./buildCommon'); 10 | const hookRunner = require('./hookRunner'); 11 | const config = require('./config'); 12 | const generateComponentsCache = require('./buildCommon/generateComponentsCache'); 13 | 14 | function _runReleaseBuildTasks(context) { 15 | return new Promise((resolve, reject) => { 16 | const opts = context.opts; 17 | if (opts.bundler === 'webpack') { 18 | hookRunner('before_release_build', context) 19 | .then(buildCommon.webpack) 20 | .then(buildCommon.cleanTemp) 21 | .then(buildCommon.cleanTypescript) 22 | .then(data => resolve(data)) 23 | .catch(err => reject(err)); 24 | } else if (opts.buildType === 'release') { 25 | hookRunner('before_release_build', context) 26 | .then(buildCommon.stripLocalCcaComponentJson) 27 | .then(buildCommon.minifyLocalCca) 28 | .then(buildCommon.restoreLocalCcaComponentJson) 29 | .then(buildCommon.minifyLocalVComponents) 30 | .then(buildCommon.terser) 31 | .then(buildCommon.requireJs) 32 | .then(buildCommon.cleanTemp) 33 | .then(buildCommon.cleanTypescript) 34 | .then(data => resolve(data)) 35 | .catch(err => reject(err)); 36 | } else { 37 | resolve(context); 38 | } 39 | }); 40 | } 41 | 42 | function _runCommonBuildTasks(context) { 43 | return buildCommon.clean(context) 44 | .then(data => hookRunner('before_build', data)) 45 | .then(buildCommon.copy) 46 | .then(buildCommon.copyLibs) 47 | .then(buildCommon.copyReferenceCca) 48 | .then(buildCommon.copyLocalCca) 49 | .then(buildCommon.copyLocalVComponents) 50 | .then(buildCommon.copyLocalResourceComponents) 51 | .then(buildCommon.spriteSvg) 52 | .then(data => hookRunner('before_injection', data)) 53 | .then(buildCommon.injectTs) 54 | .then(buildCommon.compileApplicationTypescript) 55 | .then(buildCommon.css) 56 | .then(buildCommon.injectTheme) 57 | .then(buildCommon.injectFont) 58 | .then(buildCommon.copyThemes) 59 | .then(buildCommon.injectScripts) 60 | .then(buildCommon.injectPaths) 61 | .then(data => Promise.resolve(data)) 62 | .catch(err => Promise.reject(err)); 63 | } 64 | 65 | module.exports = function buildWeb(buildType, opts) { 66 | const context = { buildType, opts, platform: 'web' }; 67 | config.set('componentsCache', generateComponentsCache({ context })); 68 | return _runCommonBuildTasks(context) 69 | .then(_runReleaseBuildTasks) 70 | .then(buildCommon.runAllComponentHooks) 71 | .then(data => hookRunner('after_build', data)) 72 | .then(data => Promise.resolve(data)) 73 | .catch(err => Promise.reject(err)); 74 | }; 75 | -------------------------------------------------------------------------------- /lib/clean.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const fs = require('fs-extra'); 10 | const config = require('./config'); 11 | const path = require('path'); 12 | 13 | const clean = function (filepath) { 14 | return new Promise((resolve, reject) => { 15 | fs.emptyDir(filepath, (err) => { 16 | if (err) { 17 | reject(err); 18 | } else { 19 | console.log(`Finished clean path ${filepath}..`); 20 | resolve(); 21 | } 22 | }); 23 | }); 24 | }; 25 | 26 | clean.platform = function () { 27 | config.loadOraclejetConfig(); 28 | clean(path.join(process.cwd(), config('paths').staging.web)); 29 | }; 30 | 31 | module.exports = clean; 32 | 33 | -------------------------------------------------------------------------------- /lib/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const fs = require('fs-extra'); 10 | const CONSTANTS = require('./constants'); 11 | const path = require('path'); 12 | 13 | const config = function (prop, value) { 14 | if (arguments.length === 2) { 15 | return config.set(prop, value); 16 | } 17 | return config.get(prop); 18 | }; 19 | module.exports = config; 20 | 21 | config.data = {}; 22 | 23 | /** 24 | * Get the config value 25 | * @param {String} prop property to get 26 | * @returns {String} return 27 | */ 28 | config.get = function (prop) { 29 | if (prop) { 30 | return config.data[prop]; 31 | } 32 | return config.data; 33 | }; 34 | 35 | /** 36 | * Set the config value 37 | * @param {String} prop property to set 38 | * @param {*} value value to set 39 | * @returns {String} return 40 | */ 41 | config.set = function (prop, value) { 42 | config.data[prop] = value; 43 | return config.data[prop]; 44 | }; 45 | 46 | config.loadOraclejetConfig = () => { 47 | config.data.paths = config.getConfiguredPaths(); 48 | }; 49 | 50 | config.getConfiguredPaths = () => { 51 | const configJson = _readConfigJson(); 52 | const src = {}; 53 | const staging = {}; 54 | 55 | const srcConfig = configJson.paths ? configJson.paths.source : {}; 56 | const stagingConfig = configJson.paths ? configJson.paths.staging : {}; 57 | 58 | src.common = srcConfig.common ? path.normalize(srcConfig.common) : CONSTANTS.APP_SRC_DIRECTORY; 59 | src.javascript = srcConfig.javascript ? path.normalize(srcConfig.javascript) : 'js'; 60 | src.typescript = srcConfig.typescript ? path.normalize(srcConfig.typescript) : 'ts'; 61 | src.styles = srcConfig.styles ? path.normalize(srcConfig.styles) : 'css'; 62 | src.themes = srcConfig.themes ? path.normalize(srcConfig.themes) : 'themes'; 63 | src.web = srcConfig.web ? path.normalize(srcConfig.web) : CONSTANTS.APP_SRC_WEB_DIRECTORY; 64 | 65 | staging.web = stagingConfig.web ? path.normalize(stagingConfig.web) : CONSTANTS.WEB_DIRECTORY; 66 | staging.themes = stagingConfig.themes ? path.normalize(stagingConfig.themes) : 67 | CONSTANTS.APP_STAGED_THEMES_DIRECTORY; 68 | 69 | staging.stagingPath = staging.web; 70 | src.platformSpecific = src.web; 71 | 72 | const components = srcConfig.components ? 73 | path.normalize(srcConfig.components) : CONSTANTS.JET_COMPOSITE_DIRECTORY; 74 | const exchangeComponents = srcConfig.exchangeComponents ? 75 | path.normalize(srcConfig.exchangeComponents) : CONSTANTS.JET_COMPONENTS_DIRECTORY; 76 | 77 | return { 78 | src, 79 | staging, 80 | components, 81 | exchangeComponents 82 | }; 83 | }; 84 | 85 | function _readConfigJson() { 86 | const configPath = path.join(process.cwd(), CONSTANTS.ORACLE_JET_CONFIG_JSON); 87 | const configJson = fs.existsSync(configPath) ? fs.readJsonSync(configPath) : {}; 88 | config.set('defaultBrowser', configJson.defaultBrowser || CONSTANTS.DEFAULT_BROWSER); 89 | config.set('sassVer', configJson.sassVer || CONSTANTS.SASS_VER); 90 | config.set('fontUrl', configJson.fontUrl || CONSTANTS.FONT_URL); 91 | config.set('typescriptLibraries', configJson.typescriptLibraries || CONSTANTS.TYPESCRIPT_LIBARIES); 92 | config.set('defaultTheme', configJson.defaultTheme || CONSTANTS.DEFAULT_THEME); 93 | config.set('installer', configJson.installer || CONSTANTS.DEFAULT_INSTALLER); 94 | config.set('webpackLibraries', configJson.webpackLibraries || CONSTANTS.WEBPACK_LIBRARIES); 95 | config.set('jsdocLibraries', configJson.jsdocLibraries || CONSTANTS.JSDOC_LIBRARIES); 96 | config.set('jestTestingLibraries', configJson.jestTestingLibraries); 97 | // We need to add typescript libraries because karma-typescript has 98 | // a dependency on typescript. And since we do not add typescript 99 | // when creating a JS app, then we will need to do so when add the 100 | // testing libraries. 101 | config.set('mochaTestingLibraries', `${configJson.mochaTestingLibraries} ${configJson.typescriptLibraries}`); 102 | 103 | return configJson; 104 | } 105 | -------------------------------------------------------------------------------- /lib/configure.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const exchange = require('./scopes/exchange'); 15 | const CONSTANTS = require('./constants'); 16 | const util = require('./util'); 17 | 18 | /** 19 | * # Switch for 'ojet.configure()' 20 | * 21 | * @public 22 | * @param {string} scope 23 | * @param {Object} parameters 24 | */ 25 | module.exports = function (scope, parameters) { 26 | switch (scope) { 27 | case (CONSTANTS.API_SCOPES.EXCHANGE): { 28 | const exchangeUrl = CONSTANTS.EXCHANGE_URL_PARAM; 29 | if (parameters && util.hasProperty(parameters, exchangeUrl)) { 30 | return exchange.configureUrl(parameters[exchangeUrl]); 31 | } 32 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.CONFIGURE}() '${exchangeUrl}' parameter.`); 33 | return false; 34 | } 35 | default: 36 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.CONFIGURE}() 'scope' parameter.`); 37 | return false; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /lib/constants.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const ORACLET_ROOT_PATH = 'node_modules/@oracle'; 10 | const ORACLEJET_PATH = `${ORACLET_ROOT_PATH}/oraclejet`; 11 | 12 | module.exports = { 13 | WEB_DIRECTORY: 'web', 14 | APP_SRC_DIRECTORY: 'src', 15 | APP_SRC_WEB_DIRECTORY: 'src-web', 16 | PACKAGE_OUTPUT_DIRECTORY: 'dist', 17 | APP_THEMES_DIRECTORY: 'themes', 18 | APP_STAGED_THEMES_DIRECTORY: 'staged-themes', 19 | DEFAULT_THEME: 'alta', 20 | DEFAULT_PCSS_THEME: 'redwood', 21 | REDWOOD_NOTAG_THEME: 'redwood-notag', 22 | DEFAULT_STABLE_THEME: 'stable', 23 | DEFAULT_BROWSER: 'chrome', 24 | DEFAULT_INSTALLER: 'npm', 25 | SASS_VER: '1.80.5', 26 | COMMON_THEME_DIRECTORY: 'common', 27 | JET_COMPOSITE_DIRECTORY: 'jet-composites', 28 | JET_COMPONENTS_DIRECTORY: 'jet_components', 29 | COMPONENT_TEMP_ARCHIVE: 'component.zip', 30 | PUBLISH_TEMP_DIRECTORY: 'temp-publish', 31 | NODE_MODULES_DIRECTORY: 'node_modules', 32 | JET_COMPONENT_JSON: 'component.json', 33 | SUPPORTED_PLATFORMS: ['android', 'ios', 'web', 'windows'], 34 | SUPPORTED_THEME_PLATFORMS: ['android', 'ios', 'web', 'windows', 'common'], 35 | SUPPORTED_BROWSERS: ['chrome', 'firefox', 'edge', 'ie', 'safari'], 36 | SUPPORTED_WEB_PLATFORMS: ['web'], 37 | 38 | APP_TYPE: 39 | { 40 | WEB: 'web' 41 | }, 42 | 43 | DEBUG_FLAG: '--debug', 44 | RELEASE_FLAG: '--release', 45 | SUPPORTED_BUILD_DESTINATIONS: ['emulator', 'device'], 46 | SUPPORTED_SERVE_WEB_DESTINATIONS: ['server-only'], 47 | DEFAULT_BUILD_DESTINATION: 'emulator', 48 | RESERVED_Key_ALL_THEME: 'all', 49 | ORACLE_JET_CONFIG_JSON: 'oraclejetconfig.json', 50 | PATH_TO_ORACLEJET: `${ORACLEJET_PATH}/dist`, 51 | PATH_MAPPING_JSON: 'path_mapping.json', 52 | PATH_MAPPING_VERSION_TOKEN: '#{version}', 53 | PATH_TO_HOOKS_CONFIG: 'scripts/hooks/hooks.json', 54 | TSCONFIG: 'tsconfig.json', 55 | OJET_CONFIG: 'ojet.config.js', 56 | APIDOC_TEMPLATES: 'apidoc_templates', 57 | TOOLING_PATH: `${ORACLET_ROOT_PATH}/oraclejet-tooling`, 58 | ORACLEJET_TOOLING_NAME: '@oracle/oraclejet-tooling', 59 | ORACLEJET_PATH, 60 | ORACLEJET_NAME: '@oracle/oraclejet', 61 | PATH_TO_PWA_TEMPLATES: 'lib/templates/serviceWorkers', 62 | PATH_TO_TSCONFIG_TEMPLATE: 'lib/templates/typescript/tsconfig.json', 63 | PATH_TO_OJET_CONFIG_TEMPLATE: 'lib/templates/webpack/ojet.config.js', 64 | API_TASKS: { 65 | ADD: 'add', 66 | ADDSASS: 'addsass', 67 | CONFIGURE: 'configure', 68 | CREATE: 'create', 69 | LIST: 'list', 70 | ADDPCSS: 'addpcss', 71 | PUBLISH: 'publish', 72 | REMOVE: 'remove', 73 | SEARCH: 'search', 74 | ADDTYPESCRIPT: 'addtypescript', 75 | ADDJSDOC: 'addjsdoc', 76 | ADDPWA: 'addpwa', 77 | PACKAGE: 'package', 78 | ADDWEBPACK: 'addwebpack', 79 | ADDTESTING: 'addtesting', 80 | MIGRATE: 'migrate' 81 | }, 82 | API_SCOPES: { 83 | EXCHANGE: 'exchange', 84 | COMPONENT: 'component', 85 | PACK: 'pack' 86 | }, 87 | EXCHANGE_URL_PARAM: 'exchange-url', 88 | EXCHANGE_LOCAL_COMPONENTS_SUPPORT: 'localComponentsSupport', 89 | OJET_LOCAL_STORAGE_DIR: '.ojet', 90 | EXCHANGE_TOKEN_STORE_FILE: 'exchange-access.json', 91 | EXCHANGE_URL_FILE: 'exchange-url.json', 92 | EXCHANGE_GLOBAL_URL_KEY: 'global', 93 | EXCHANGE_HEADER_NEXT_TOKEN: 'x-compcatalog-auth-next-token', 94 | EXCHANGE_HEADER_NEXT_TOKEN_EXPIRATION: 'x-compcatalog-auth-next-token-expiration', 95 | EXCHANGE_AUTH_ACCESS_TOKEN: 'access_token', 96 | EXCHANGE_AUTH_TOKEN_TYPE: 'token_type', 97 | EXCHANGE_AUTH_EXPIRATION: 'expiration', 98 | EXCHANGE_AUTH_EXPIRATION_CLIENT: 'expiration_client', 99 | EXCHANGE_AUTH_EXPIRES_IN: 'expires_in', 100 | COMPONENTS_DT: 'components_dt', 101 | PATH_TO_CUSTOM_TSC: 'dist/custom-tsc', 102 | PATH_TO_CUSTOM_TSC_TEMPLATES: 'dist/custom-tsc/templates', 103 | PATH_TO_JSDOC: 'dist/jsdoc', 104 | JSDOC_CONFIG_JSON: 'confapidoc.json', 105 | JSDOC_LIBRARIES: 'jsdoc@3.5.5', 106 | TYPESCRIPT_LIBARIES: 'typescript@5.7.2 yargs-parser@~13.1.2', 107 | JEST_TEST_FILE_AND_LIBS_GLOBS: ['**/__tests__/**/*.[jt]s?(x)', '**/?(*.)+(spec|test).[jt]s?(x)', 'libs/**'], 108 | OMIT_COMPONENT_VERSION_FLAG: 'omit-component-version', 109 | WEBPACK_TOOLS_PATH: 'dist/webpack-tools', 110 | VDOM_ARCHITECTURE: 'vdom', 111 | DEFAULT_BUNDLE_NAME: 'bundle.js', 112 | PATH_MAPPING_PREFIX_TOKEN: '#', 113 | COMPONENT_JSON_DEPENDENCIES_TOKEN: '@dependencies@', 114 | MONO_PACK_CONTENTS_TOKEN: '@contents@', 115 | PATH_TO_OJET_CONFIG: './ojet.config.js', 116 | FONT_URL: 'https://static.oracle.com/cdn/fnd/gallery/2404.0.0/images/iconfont/ojuxIconFont.min.css', 117 | WEBPACK_LIBRARIES: 'webpack@5.76.0 @types/node@18.16.3 webpack-dev-server style-loader css-loader sass-loader sass ts-loader@8.4.0 raw-loader noop-loader html-webpack-plugin html-replace-webpack-plugin copy-webpack-plugin @prefresh/webpack @prefresh/babel-plugin webpack-merge compression-webpack-plugin mini-css-extract-plugin clean-webpack-plugin css-fix-url-loader zlib', 118 | ANSI_CSI_START: '\u001b[', 119 | ANSI_CS_FGCOLOR_LEN: 5, 120 | 121 | COMPONENT_TYPE: { 122 | REFERENCE: 'reference', 123 | RESOURCE: 'resource', 124 | VBCS_PATTERN: 'vbcs-pattern' 125 | }, 126 | 127 | PACK_TYPE: { 128 | MONO_PACK: 'mono-pack', 129 | } 130 | }; 131 | -------------------------------------------------------------------------------- /lib/create.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const CONSTANTS = require('./constants'); 15 | const pack = require('./scopes/pack'); 16 | const util = require('./util'); 17 | 18 | /** 19 | * # Switch for 'ojet.create()' 20 | * 21 | * @public 22 | * @param {string} scope 23 | * @param {string} parameter 24 | * @returns {Promise} 25 | */ 26 | module.exports = function (scope, parameter, options) { 27 | switch (scope) { 28 | case (CONSTANTS.API_SCOPES.PACK): 29 | return pack.create(parameter, options); 30 | default: 31 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.CREATE}() 'scope' parameter.`); 32 | return false; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /lib/hookRunner.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const path = require('path'); 10 | const fs = require('fs-extra'); 11 | const CONSTANTS = require('./constants'); 12 | const util = require('./util'); 13 | 14 | module.exports = function runHooks(type, context) { 15 | const hooksConfig = _getHooksConfigObj().hooks; 16 | return _getHookPromise(type, hooksConfig, context); 17 | }; 18 | 19 | // return the promise defined in the hooks. If not defined, return empty promise 20 | function _getHookPromise(type, hooksConfig, context) { 21 | return new Promise((resolve, reject) => { 22 | const updatedContext = util.processContextForHooks(context); 23 | const hookPath = (hooksConfig && Object.prototype.hasOwnProperty.call(hooksConfig, type)) ? 24 | path.resolve(hooksConfig[type]) : undefined; 25 | if (hookPath && util.fsExistsSync(hookPath)) { 26 | const hook = require(hookPath); // eslint-disable-line 27 | hook(updatedContext) 28 | .then((newContext) => { 29 | if (newContext === undefined || newContext === null || 30 | (Object.keys(newContext).length === 0 && newContext.constructor === Object)) { 31 | // If context coming back is null or empty, 32 | // likely a mistake in the hook implementation that needs to be flagged 33 | util.log.warning(`Hook ${type} returning null or empty context. Returning original context`); 34 | resolve(context); 35 | } else { 36 | resolve(newContext); 37 | } 38 | }) 39 | .catch(err => reject(err)); 40 | } else { 41 | console.log(`Hook ${type} not defined..`); 42 | resolve(context); 43 | } 44 | }); 45 | } 46 | 47 | // Read the hooks.json file 48 | function _getHooksConfigObj() { 49 | const file = path.resolve(CONSTANTS.PATH_TO_HOOKS_CONFIG); 50 | if (util.fsExistsSync(file)) { 51 | return fs.readJsonSync(file); 52 | } 53 | return {}; 54 | } 55 | -------------------------------------------------------------------------------- /lib/injectorUtil.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const util = require('./util'); 10 | 11 | module.exports = { 12 | getInjectorTagsRegExp: _getInjectorTagsRegExp, 13 | getLineEnding: _getLineEnding, 14 | replaceInjectorTokens: _replaceInjectorTokens, 15 | createScriptElementString: _createScriptElementString 16 | }; 17 | 18 | function _getInjectorTagsRegExp(starttag, endtag) { 19 | const start = _escapeForRegExp(starttag); 20 | const end = _escapeForRegExp(endtag); 21 | const startNoSpace = _escapeForRegExp(starttag.replace(/\s/g, '')); 22 | const endNoSpace = _escapeForRegExp(endtag.replace(/\s/g, '')); 23 | return new RegExp(`([\t ]*)(${start}|${startNoSpace})((\\n|\\r|.)*?)(${end}|${endNoSpace})`, 'gi'); 24 | } 25 | 26 | function _escapeForRegExp(str) { 27 | return str.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&'); 28 | } 29 | 30 | function _getLineEnding(content) { 31 | return /\r\n/.test(String(content)) ? '\r\n' : '\n'; 32 | } 33 | 34 | function _checkTags(content, startTag, endTag) { 35 | // Check for one or the other missing--both may be ok 36 | const missingStartTag = startTag && content.indexOf(startTag) < 0; 37 | const missingEndTag = endTag && content.indexOf(endTag) < 0; 38 | if (missingStartTag && !missingEndTag) { 39 | util.log.error(`Missing start tag '${startTag}'`); 40 | } else if (!missingStartTag && missingEndTag) { 41 | util.log.error(`Missing end tag '${endTag}'`); 42 | } 43 | } 44 | 45 | function _replaceInjectorTokens({ content, pattern, replace, eol, startTag, endTag }) { 46 | // Check for tags 47 | _checkTags(content, startTag, endTag); 48 | 49 | // remove the existing content, if any 50 | let injectResult = content.replace(pattern, () => 51 | startTag + eol + endTag 52 | ); 53 | // actual injection of new content 54 | injectResult = injectResult.replace(pattern, () => 55 | startTag + eol + replace + eol + endTag 56 | ); 57 | return injectResult; 58 | } 59 | 60 | function _createScriptElementString(scriptSrc, type) { 61 | const scriptType = type === null ? 'text/javascript' : type; 62 | return ``; 63 | } 64 | -------------------------------------------------------------------------------- /lib/label.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const CONSTANTS = require('./constants'); 15 | const util = require('./util'); 16 | const component = require('./scopes/component'); 17 | 18 | /** 19 | * # Adds label to given component in exchange' 20 | * 21 | * @public 22 | * @param {string} scope 23 | * @returns {Promise} 24 | */ 25 | module.exports = function (scope, parameter, option) { 26 | switch (scope) { 27 | case (CONSTANTS.API_SCOPES.COMPONENT): 28 | case (CONSTANTS.API_SCOPES.PACK): 29 | return component.label(parameter, option); 30 | default: 31 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.LIST}() 'scope' parameter.`); 32 | return false; 33 | } 34 | }; 35 | -------------------------------------------------------------------------------- /lib/list.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const component = require('./scopes/component'); 15 | const CONSTANTS = require('./constants'); 16 | const pack = require('./scopes/pack'); 17 | const util = require('./util'); 18 | 19 | /** 20 | * # Switch for 'ojet.list()' 21 | * 22 | * @public 23 | * @param {string} scope 24 | * @returns {Promise} 25 | */ 26 | module.exports = function (scope) { 27 | switch (scope) { 28 | case (CONSTANTS.API_SCOPES.COMPONENT): 29 | return component.list(); 30 | case (CONSTANTS.API_SCOPES.PACK): 31 | return pack.list(); 32 | default: 33 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.LIST}() 'scope' parameter.`); 34 | return false; 35 | } 36 | }; 37 | -------------------------------------------------------------------------------- /lib/npmCopy.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const fs = require('fs-extra'); 10 | const path = require('path'); 11 | const config = require('./config'); 12 | const CONSTANTS = require('./constants'); 13 | const util = require('./util'); 14 | const configGenerator = require('./rjsConfigGenerator'); 15 | 16 | module.exports = {}; 17 | const npmCopy = module.exports; 18 | 19 | /** 20 | * # npmCopy 21 | * To align with Jet's 3rd party library directory structure, this config will copy from its 22 | * original npm location to specific path with the file name being modified to include the 23 | * version string. 24 | */ 25 | npmCopy.getNonMappingFileList = function (buildType) { 26 | // Remove the @oracle/oraclejet portion after finding it 27 | const srcPrefix = path.join(path.dirname(path.dirname(util.getOraclejetPath())), path.sep); 28 | const destPrefix = `${config('paths').staging.web}/${config('paths').src.javascript}/libs`; 29 | const cssSrcPrefix = `${srcPrefix}/@oracle/oraclejet/dist/css/`; 30 | const cssCorePackSrcPrefix = `${srcPrefix}/@oracle/oraclejet-preact/amd/`; 31 | let nonMappingList = []; 32 | const versions = util.getLibVersionsObj(); 33 | if (!util.getInstalledCssPackage()) { 34 | if (config('defaultTheme') === CONSTANTS.DEFAULT_PCSS_THEME) { 35 | nonMappingList = [ 36 | { 37 | cwd: `${srcPrefix}requirejs`, 38 | src: ['*.js'], 39 | dest: `${destPrefix}/require` 40 | }, 41 | { 42 | cwd: `${cssSrcPrefix}alta`, 43 | src: ['**'], 44 | dest: `${config('paths').staging.themes}/alta/web` 45 | }, 46 | { 47 | cwd: `${cssSrcPrefix}alta-windows`, 48 | src: ['**'], 49 | dest: `${config('paths').staging.themes}/alta/windows` 50 | }, 51 | { 52 | cwd: `${cssSrcPrefix}alta-android`, 53 | src: ['**'], 54 | dest: `${config('paths').staging.themes}/alta/android` 55 | }, 56 | { 57 | cwd: `${cssSrcPrefix}alta-ios`, 58 | src: ['**'], 59 | dest: `${config('paths').staging.themes}/alta/ios` 60 | }, 61 | { 62 | cwd: `${cssSrcPrefix}common`, 63 | src: ['**'], 64 | dest: `${config('paths').staging.themes}/alta/common` 65 | }, 66 | { 67 | cwd: `${srcPrefix}@oracle/oraclejet/dist/js/libs/oj/resources/nls`, 68 | src: ['*.js'], 69 | dest: `${config('paths').staging.stagingPath}/${config('paths').src.javascript}/libs/oj/${util.getJETVersionV(versions.ojs)}/resources/root` 70 | }, 71 | { 72 | cwd: `${cssSrcPrefix}redwood`, 73 | src: ['**'], 74 | dest: `${config('paths').staging.themes}/redwood/web` 75 | }, 76 | { 77 | cwd: `${cssSrcPrefix}stable`, 78 | src: ['**'], 79 | dest: `${config('paths').staging.themes}/stable/web` 80 | }, 81 | { 82 | cwd: `${cssCorePackSrcPrefix}Theme-redwood`, 83 | src: ['*.css'], 84 | dest: `${config('paths').staging.themes}/theme-redwood/web` 85 | }, 86 | { 87 | cwd: `${cssCorePackSrcPrefix}images`, 88 | src: ['**'], 89 | dest: `${config('paths').staging.themes}/theme-redwood/web/images` 90 | }, 91 | { 92 | cwd: `${cssCorePackSrcPrefix}Theme-stable`, 93 | src: ['*.css'], 94 | dest: `${config('paths').staging.themes}/theme-stable/web` 95 | }, 96 | { 97 | cwd: `${cssCorePackSrcPrefix}images`, 98 | src: ['**'], 99 | dest: `${config('paths').staging.themes}/theme-stable/web/images` 100 | } 101 | ]; 102 | } else { 103 | nonMappingList = [ 104 | { 105 | cwd: `${srcPrefix}requirejs`, 106 | src: ['*.js'], 107 | dest: `${destPrefix}/require` 108 | }, 109 | { 110 | cwd: `${cssSrcPrefix}alta`, 111 | src: ['**'], 112 | dest: `${config('paths').staging.themes}/alta/web` 113 | }, 114 | { 115 | cwd: `${cssSrcPrefix}alta-windows`, 116 | src: ['**'], 117 | dest: `${config('paths').staging.themes}/alta/windows` 118 | }, 119 | { 120 | cwd: `${cssSrcPrefix}alta-android`, 121 | src: ['**'], 122 | dest: `${config('paths').staging.themes}/alta/android` 123 | }, 124 | { 125 | cwd: `${cssSrcPrefix}alta-ios`, 126 | src: ['**'], 127 | dest: `${config('paths').staging.themes}/alta/ios` 128 | }, 129 | { 130 | cwd: `${cssSrcPrefix}common`, 131 | src: ['**'], 132 | dest: `${config('paths').staging.themes}/alta/common` 133 | }, 134 | { 135 | cwd: `${cssCorePackSrcPrefix}Theme-redwood`, 136 | src: ['*.css'], 137 | dest: `${config('paths').staging.themes}/theme-redwood/web` 138 | }, 139 | { 140 | cwd: `${cssCorePackSrcPrefix}images`, 141 | src: ['**'], 142 | dest: `${config('paths').staging.themes}/theme-redwood/web/images` 143 | }, 144 | { 145 | cwd: `${cssCorePackSrcPrefix}Theme-stable`, 146 | src: ['*.css'], 147 | dest: `${config('paths').staging.themes}/theme-stable/web` 148 | }, 149 | { 150 | cwd: `${cssCorePackSrcPrefix}images`, 151 | src: ['**'], 152 | dest: `${config('paths').staging.themes}/theme-stable/web/images` 153 | }, 154 | { 155 | cwd: `${srcPrefix}@oracle/oraclejet/dist/js/libs/oj/resources/nls`, 156 | src: ['*.js'], 157 | dest: `${config('paths').staging.stagingPath}/${config('paths').src.javascript}/libs/oj/${util.getJETVersionV(versions.ojs)}/resources/root` 158 | } 159 | ]; 160 | } 161 | } else { 162 | nonMappingList = [ 163 | { 164 | cwd: `${srcPrefix}requirejs`, 165 | src: ['*.js'], 166 | dest: `${destPrefix}/require` 167 | }, 168 | { 169 | cwd: `${cssSrcPrefix}redwood`, 170 | src: ['**'], 171 | dest: `${config('paths').staging.themes}/redwood/web` 172 | }, 173 | { 174 | cwd: `${cssSrcPrefix}stable`, 175 | src: ['**'], 176 | dest: `${config('paths').staging.themes}/stable/web` 177 | }, 178 | { 179 | cwd: `${cssCorePackSrcPrefix}Theme-redwood`, 180 | src: ['*.css'], 181 | dest: `${config('paths').staging.themes}/theme-redwood/web` 182 | }, 183 | { 184 | cwd: `${cssCorePackSrcPrefix}images`, 185 | src: ['**'], 186 | dest: `${config('paths').staging.themes}/theme-redwood/web/images` 187 | }, 188 | { 189 | cwd: `${cssCorePackSrcPrefix}Theme-stable`, 190 | src: ['*.css'], 191 | dest: `${config('paths').staging.themes}/theme-stable/web` 192 | }, 193 | { 194 | cwd: `${cssCorePackSrcPrefix}images`, 195 | src: ['**'], 196 | dest: `${config('paths').staging.themes}/theme-stable/web/images` 197 | }, 198 | { 199 | cwd: `${srcPrefix}@oracle/oraclejet/dist/js/libs/oj/resources/nls`, 200 | src: ['*.js'], 201 | dest: `${config('paths').staging.stagingPath}/${config('paths').src.javascript}/libs/oj/${util.getJETVersionV(versions.ojs)}/resources/root` 202 | } 203 | ]; 204 | } 205 | 206 | nonMappingList = util.getFileList(buildType, nonMappingList); 207 | 208 | return nonMappingList; 209 | }; 210 | 211 | function _getDestPrefix() { 212 | return `${config('paths').staging.web}`; 213 | } 214 | 215 | function _getValidLibObj(buildType, libName, libObj, base, platform) { 216 | let cwd = libObj.cwd; 217 | if (libObj[buildType].cwd !== undefined) cwd = path.join(libObj.cwd, libObj[buildType].cwd); 218 | const nodeModulesPath = path.dirname(path.dirname(util.getOraclejetPath())); 219 | const newCwd = cwd.replace('node_modules', nodeModulesPath); 220 | const src = Array.isArray(libObj[buildType].src) 221 | ? libObj[buildType].src : [libObj[buildType].src]; 222 | const dest = _getValidDestFromPathMapping(libObj[buildType], libName, base, platform); 223 | if (_needRename(libObj[buildType].src, dest)) { 224 | const rename = function (pathPrefix) { 225 | const fileName = path.basename(_processVersionToken(libName, libObj[buildType].path)); 226 | return path.join(pathPrefix, fileName); 227 | }; 228 | return { cwd: newCwd, src, dest, rename }; 229 | } 230 | return { cwd: newCwd, src, dest }; 231 | } 232 | 233 | function _needRename(src, dest) { 234 | // when the provided src is a single file, and the requirejs path has a different name 235 | // example in node moduels, the lib is jquery.js, but the path is jquery-3.3.1.js 236 | if (Array.isArray(src)) return false; 237 | return path.basename(src) !== path.basename(dest); 238 | } 239 | 240 | function _getValidDestFromPathMapping(libObj, libName, base, platform) { 241 | let dest = (path.extname(libObj.path) === '' || path.extname(libObj.path) === '.min') 242 | ? libObj.path : path.join(libObj.path, '..'); 243 | dest = _processVersionToken(libName, dest); 244 | return path.join(_getDestPrefix(platform), base, dest); 245 | } 246 | 247 | function _processVersionToken(libName, destPath) { 248 | const versions = util.getLibVersionsObj(); 249 | 250 | return Object.keys(versions).indexOf(libName) !== -1 251 | ? util.replaceVersionToken(destPath, versions, libName) : destPath; 252 | } 253 | 254 | function _needCopyTask(buildType, lib, libObj) { 255 | if (!libObj[buildType]) { 256 | util.log.error(`The path mapping entry (${lib}) is missing an entry for the \"${buildType}\" property. Build failed.`); // eslint-disable-line 257 | } 258 | return Object.prototype.hasOwnProperty.call(libObj[buildType], 'src'); 259 | } 260 | 261 | npmCopy.renameAltaThemeFiles = function (paths) { 262 | if (!util.getInstalledCssPackage()) { 263 | const fileList = { 264 | 'oj-alta.css': 'alta.css', 265 | 'oj-alta-min.css': 'alta.min.css' 266 | }; 267 | 268 | const themePath = path.join(paths.staging.themes, CONSTANTS.DEFAULT_THEME); 269 | 270 | CONSTANTS.SUPPORTED_PLATFORMS.forEach((platform) => { 271 | Object.keys(fileList).forEach((key) => { 272 | fs.renameSync(path.join(themePath, platform, key), 273 | path.join(themePath, platform, fileList[key])); 274 | }); 275 | }); 276 | 277 | if (config('defaultTheme') === CONSTANTS.DEFAULT_PCSS_THEME) { 278 | const redwoodfileList = { 279 | 'oj-redwood.css': 'redwood.css', 280 | 'oj-redwood-min.css': 'redwood.min.css', 281 | 'oj-redwood-notag.css': 'redwood-notag.css', 282 | 'oj-redwood-notag-min.css': 'redwood-notag.min.css' 283 | }; 284 | const redwoodthemePath = path.join(paths.staging.themes, CONSTANTS.DEFAULT_PCSS_THEME); 285 | Object.keys(redwoodfileList).forEach((key) => { 286 | fs.renameSync(path.join(redwoodthemePath, 'web', key), 287 | path.join(redwoodthemePath, 'web', redwoodfileList[key])); 288 | }); 289 | 290 | const stablefileList = { 291 | 'oj-stable.css': 'stable.css', 292 | 'oj-stable-min.css': 'stable.min.css' 293 | }; 294 | const stablethemePath = path.join(paths.staging.themes, CONSTANTS.DEFAULT_STABLE_THEME); 295 | Object.keys(stablefileList).forEach((key) => { 296 | fs.renameSync(path.join(stablethemePath, 'web', key), 297 | path.join(stablethemePath, 'web', stablefileList[key])); 298 | }); 299 | } 300 | } else { 301 | const fileList = { 302 | 'oj-redwood.css': 'redwood.css', 303 | 'oj-redwood-min.css': 'redwood.min.css', 304 | 'oj-redwood-notag.css': 'redwood-notag.css', 305 | 'oj-redwood-notag-min.css': 'redwood-notag.min.css' 306 | }; 307 | 308 | const themePath = path.join(paths.staging.themes, CONSTANTS.DEFAULT_PCSS_THEME); 309 | Object.keys(fileList).forEach((key) => { 310 | fs.renameSync(path.join(themePath, 'web', key), 311 | path.join(themePath, 'web', fileList[key])); 312 | }); 313 | const stablefileList = { 314 | 'oj-stable.css': 'stable.css', 315 | 'oj-stable-min.css': 'stable.min.css' 316 | }; 317 | 318 | const stablethemePath = path.join(paths.staging.themes, CONSTANTS.DEFAULT_STABLE_THEME); 319 | Object.keys(stablefileList).forEach((key) => { 320 | fs.renameSync(path.join(stablethemePath, 'web', key), 321 | path.join(stablethemePath, 'web', stablefileList[key])); 322 | }); 323 | } 324 | }; 325 | 326 | npmCopy.getMappingLibsList = function (buildMode, platform) { 327 | const buildType = buildMode === 'release' ? 'release' : 'debug'; 328 | const libsList = []; 329 | const masterJson = util.readPathMappingJson(); 330 | const basePath = masterJson.baseUrl; 331 | Object.keys(masterJson.libs || {}).forEach((lib) => { 332 | const libObj = masterJson.libs[lib]; 333 | const isCdn = configGenerator.isCdnPath( 334 | libObj, 335 | masterJson.use, 336 | masterJson.cdns, 337 | buildType, 338 | lib 339 | ); 340 | // Skip copy the library if it uses cdn 341 | if (!isCdn && _needCopyTask(buildType, lib, libObj)) { 342 | libsList.push(_getValidLibObj(buildType, lib, libObj, basePath, platform)); 343 | } 344 | }); 345 | return libsList; 346 | }; 347 | -------------------------------------------------------------------------------- /lib/package.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const component = require('./scopes/component'); 15 | const CONSTANTS = require('./constants'); 16 | const pack = require('./scopes/pack'); 17 | const util = require('./util'); 18 | 19 | /** 20 | * # Switch for 'ojet.package()' 21 | * 22 | * @public 23 | * @param {string} scope 24 | * @param {Array} parameters 25 | * @param {Object} options 26 | * @returns {Promise} 27 | */ 28 | class Package { 29 | package(scope, parameters, options) { // eslint-disable-line 30 | switch (scope) { 31 | case (CONSTANTS.API_SCOPES.COMPONENT): 32 | return component.package(parameters, options); 33 | case (CONSTANTS.API_SCOPES.PACK): 34 | return pack.package(parameters, options); 35 | default: 36 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.PACKAGE}() 'scope' parameter.`); 37 | return Promise.reject(); 38 | } 39 | } 40 | } 41 | 42 | module.exports = Package; 43 | -------------------------------------------------------------------------------- /lib/parser/dom-parser.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | function DOMParser(options){ 8 | this.options = options ||{locator:{}}; 9 | 10 | } 11 | DOMParser.prototype.parseFromString = function(source,mimeType){ 12 | var options = this.options; 13 | var sax = new XMLReader(); 14 | var domBuilder = options.domBuilder || new DOMHandler();//contentHandler and LexicalHandler 15 | var errorHandler = options.errorHandler; 16 | var locator = options.locator; 17 | var defaultNSMap = options.xmlns||{}; 18 | var entityMap = {'lt':'<','gt':'>','amp':'&','quot':'"','apos':"'"} 19 | if(locator){ 20 | domBuilder.setDocumentLocator(locator) 21 | } 22 | 23 | sax.errorHandler = buildErrorHandler(errorHandler,domBuilder,locator); 24 | sax.domBuilder = options.domBuilder || domBuilder; 25 | if(/\/x?html?$/.test(mimeType)){ 26 | entityMap.nbsp = '\xa0'; 27 | entityMap.copy = '\xa9'; 28 | defaultNSMap['']= 'http://www.w3.org/1999/xhtml'; 29 | } 30 | defaultNSMap.xml = defaultNSMap.xml || 'http://www.w3.org/XML/1998/namespace'; 31 | if(source){ 32 | sax.parse(source,defaultNSMap,entityMap); 33 | }else{ 34 | sax.errorHandler.error("invalid doc source"); 35 | } 36 | return domBuilder.doc; 37 | } 38 | function buildErrorHandler(errorImpl,domBuilder,locator){ 39 | if(!errorImpl){ 40 | if(domBuilder instanceof DOMHandler){ 41 | return domBuilder; 42 | } 43 | errorImpl = domBuilder ; 44 | } 45 | var errorHandler = {} 46 | var isCallback = errorImpl instanceof Function; 47 | locator = locator||{} 48 | function build(key){ 49 | var fn = errorImpl[key]; 50 | if(!fn && isCallback){ 51 | fn = errorImpl.length == 2?function(msg){errorImpl(key,msg)}:errorImpl; 52 | } 53 | errorHandler[key] = fn && function(msg){ 54 | fn('[xmldom '+key+']\t'+msg+_locator(locator)); 55 | }||function(){}; 56 | } 57 | build('warning'); 58 | build('error'); 59 | build('fatalError'); 60 | return errorHandler; 61 | } 62 | 63 | //console.log('#\n\n\n\n\n\n\n####') 64 | /** 65 | * +ContentHandler+ErrorHandler 66 | * +LexicalHandler+EntityResolver2 67 | * -DeclHandler-DTDHandler 68 | * 69 | * DefaultHandler:EntityResolver, DTDHandler, ContentHandler, ErrorHandler 70 | * DefaultHandler2:DefaultHandler,LexicalHandler, DeclHandler, EntityResolver2 71 | * @link http://www.saxproject.org/apidoc/org/xml/sax/helpers/DefaultHandler.html 72 | */ 73 | function DOMHandler() { 74 | this.cdata = false; 75 | } 76 | function position(locator,node){ 77 | node.lineNumber = locator.lineNumber; 78 | node.columnNumber = locator.columnNumber; 79 | } 80 | /** 81 | * @see org.xml.sax.ContentHandler#startDocument 82 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ContentHandler.html 83 | */ 84 | DOMHandler.prototype = { 85 | startDocument : function() { 86 | this.doc = new DOMImplementation().createDocument(null, null, null); 87 | if (this.locator) { 88 | this.doc.documentURI = this.locator.systemId; 89 | } 90 | }, 91 | startElement:function(namespaceURI, localName, qName, attrs) { 92 | var doc = this.doc; 93 | var el = doc.createElementNS(namespaceURI, qName||localName); 94 | var len = attrs.length; 95 | appendElement(this, el); 96 | this.currentElement = el; 97 | 98 | this.locator && position(this.locator,el) 99 | for (var i = 0 ; i < len; i++) { 100 | var namespaceURI = attrs.getURI(i); 101 | var value = attrs.getValue(i); 102 | var qName = attrs.getQName(i); 103 | var attr = doc.createAttributeNS(namespaceURI, qName); 104 | this.locator &&position(attrs.getLocator(i),attr); 105 | attr.value = attr.nodeValue = value; 106 | el.setAttributeNode(attr) 107 | } 108 | }, 109 | endElement:function(namespaceURI, localName, qName) { 110 | var current = this.currentElement 111 | var tagName = current.tagName; 112 | this.currentElement = current.parentNode; 113 | }, 114 | startPrefixMapping:function(prefix, uri) { 115 | }, 116 | endPrefixMapping:function(prefix) { 117 | }, 118 | processingInstruction:function(target, data) { 119 | var ins = this.doc.createProcessingInstruction(target, data); 120 | this.locator && position(this.locator,ins) 121 | appendElement(this, ins); 122 | }, 123 | ignorableWhitespace:function(ch, start, length) { 124 | }, 125 | characters:function(chars, start, length) { 126 | chars = _toString.apply(this,arguments) 127 | //console.log(chars) 128 | if(chars){ 129 | if (this.cdata) { 130 | var charNode = this.doc.createCDATASection(chars); 131 | } else { 132 | var charNode = this.doc.createTextNode(chars); 133 | } 134 | if(this.currentElement){ 135 | this.currentElement.appendChild(charNode); 136 | }else if(/^\s*$/.test(chars)){ 137 | this.doc.appendChild(charNode); 138 | //process xml 139 | } 140 | this.locator && position(this.locator,charNode) 141 | } 142 | }, 143 | skippedEntity:function(name) { 144 | }, 145 | endDocument:function() { 146 | this.doc.normalize(); 147 | }, 148 | setDocumentLocator:function (locator) { 149 | if(this.locator = locator){// && !('lineNumber' in locator)){ 150 | locator.lineNumber = 0; 151 | } 152 | }, 153 | //LexicalHandler 154 | comment:function(chars, start, length) { 155 | chars = _toString.apply(this,arguments) 156 | var comm = this.doc.createComment(chars); 157 | this.locator && position(this.locator,comm) 158 | appendElement(this, comm); 159 | }, 160 | 161 | startCDATA:function() { 162 | //used in characters() methods 163 | this.cdata = true; 164 | }, 165 | endCDATA:function() { 166 | this.cdata = false; 167 | }, 168 | 169 | startDTD:function(name, publicId, systemId) { 170 | var impl = this.doc.implementation; 171 | if (impl && impl.createDocumentType) { 172 | var dt = impl.createDocumentType(name, publicId, systemId); 173 | this.locator && position(this.locator,dt) 174 | appendElement(this, dt); 175 | } 176 | }, 177 | /** 178 | * @see org.xml.sax.ErrorHandler 179 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ErrorHandler.html 180 | */ 181 | warning:function(error) { 182 | console.warn('[xmldom warning]\t'+error,_locator(this.locator)); 183 | }, 184 | error:function(error) { 185 | console.error('[xmldom error]\t'+error,_locator(this.locator)); 186 | }, 187 | fatalError:function(error) { 188 | console.error('[xmldom fatalError]\t'+error,_locator(this.locator)); 189 | throw error; 190 | } 191 | } 192 | function _locator(l){ 193 | if(l){ 194 | return '\n@'+(l.systemId ||'')+'#[line:'+l.lineNumber+',col:'+l.columnNumber+']' 195 | } 196 | } 197 | function _toString(chars,start,length){ 198 | if(typeof chars == 'string'){ 199 | return chars.substr(start,length) 200 | }else{//java sax connect width xmldom on rhino(what about: "? && !(chars instanceof String)") 201 | if(chars.length >= start+length || start){ 202 | return new java.lang.String(chars,start,length)+''; 203 | } 204 | return chars; 205 | } 206 | } 207 | 208 | /* 209 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/LexicalHandler.html 210 | * used method of org.xml.sax.ext.LexicalHandler: 211 | * #comment(chars, start, length) 212 | * #startCDATA() 213 | * #endCDATA() 214 | * #startDTD(name, publicId, systemId) 215 | * 216 | * 217 | * IGNORED method of org.xml.sax.ext.LexicalHandler: 218 | * #endDTD() 219 | * #startEntity(name) 220 | * #endEntity(name) 221 | * 222 | * 223 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/DeclHandler.html 224 | * IGNORED method of org.xml.sax.ext.DeclHandler 225 | * #attributeDecl(eName, aName, type, mode, value) 226 | * #elementDecl(name, model) 227 | * #externalEntityDecl(name, publicId, systemId) 228 | * #internalEntityDecl(name, value) 229 | * @link http://www.saxproject.org/apidoc/org/xml/sax/ext/EntityResolver2.html 230 | * IGNORED method of org.xml.sax.EntityResolver2 231 | * #resolveEntity(String name,String publicId,String baseURI,String systemId) 232 | * #resolveEntity(publicId, systemId) 233 | * #getExternalSubset(name, baseURI) 234 | * @link http://www.saxproject.org/apidoc/org/xml/sax/DTDHandler.html 235 | * IGNORED method of org.xml.sax.DTDHandler 236 | * #notationDecl(name, publicId, systemId) {}; 237 | * #unparsedEntityDecl(name, publicId, systemId, notationName) {}; 238 | */ 239 | "endDTD,startEntity,endEntity,attributeDecl,elementDecl,externalEntityDecl,internalEntityDecl,resolveEntity,getExternalSubset,notationDecl,unparsedEntityDecl".replace(/\w+/g,function(key){ 240 | DOMHandler.prototype[key] = function(){return null} 241 | }) 242 | 243 | /* Private static helpers treated below as private instance methods, so don't need to add these to the public API; we might use a Relator to also get rid of non-standard public properties */ 244 | function appendElement (hander,node) { 245 | if (!hander.currentElement) { 246 | hander.doc.appendChild(node); 247 | } else { 248 | hander.currentElement.appendChild(node); 249 | } 250 | }//appendChild and setAttributeNS are preformance key 251 | 252 | //if(typeof require == 'function'){ 253 | var XMLReader = require('./sax').XMLReader; 254 | var DOMImplementation = exports.DOMImplementation = require('./dom').DOMImplementation; 255 | exports.XMLSerializer = require('./dom').XMLSerializer ; 256 | exports.DOMParser = DOMParser; 257 | //} 258 | -------------------------------------------------------------------------------- /lib/pcss.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const util = require('./util'); 10 | const fs = require('fs-extra'); 11 | const path = require('path'); 12 | 13 | function _getPcssPromises(context) { 14 | // compile only the changed theme during livereload 15 | if (context.changedTheme && context.changedTheme.compile) { 16 | return _getPcssTasks(context.changedTheme, context); 17 | } 18 | 19 | if (context.changedCcaTheme) { 20 | console.log(`CCA ${context.changedCcaTheme} Changed...`); 21 | return _getCcaPcssTasks(context, true); 22 | } 23 | 24 | return _getDefaultPcssThemeTasks(context); 25 | } 26 | 27 | function _getDefaultPcssThemeTasks(context) { 28 | const opts = context.opts; 29 | // compile the default theme 30 | let taskArray = []; 31 | 32 | if (opts.theme) { 33 | // compile additional multi themes 34 | if (opts.themes) { 35 | opts.themes.forEach((singleTheme) => { 36 | if (singleTheme.compile) { 37 | taskArray = taskArray.concat(_getPcssTasks(singleTheme, context)); 38 | } 39 | }); 40 | } else { 41 | taskArray = taskArray.concat(_getPcssTasks(opts.theme, context)); 42 | } 43 | } 44 | 45 | // // if svg is enabled, re-compile Alta 46 | // if ((opts.svg) && opts.theme.name === CONSTANT.DEFAULT_THEME) { 47 | // taskArray = taskArray.concat(_getAltaSassTasks(opts.theme, context)); 48 | // } 49 | 50 | // compile cca 51 | if (opts.sassCompile) { 52 | taskArray = taskArray.concat(_getCcaPcssTasks(context, false)); 53 | } 54 | 55 | return taskArray; 56 | } 57 | 58 | function _getCcaPcssTasks(context, isCcaLiveReload) { 59 | const fileList = util.getFileList(context.buildType, _getCcaFileList(context.opts.pcss.fileList)); 60 | return _getPcssTasksFromFileList(context.changedCcaTheme, fileList, context, isCcaLiveReload); 61 | } 62 | 63 | function _getPcssTasks(theme, context) { 64 | let fileList = _getPcssThemeFileList(context.opts.pcss.fileList); 65 | fileList = util.getFileList(context.buildType, fileList, { theme }, { theme }); 66 | return _getPcssTasksFromFileList(theme, fileList, context, false); 67 | } 68 | 69 | function _getCcaFileList(fileList) { 70 | return fileList.filter(fileObj => util.isCcaSassFile(fileObj.cwd)); 71 | } 72 | 73 | function _getPcssThemeFileList(fileList) { 74 | return fileList.filter(fileObj => !util.isCcaSassFile(fileObj.cwd)); 75 | } 76 | 77 | function _getPcssDest(context, buildType, dest, isReleaseBuild) { 78 | const name = path.basename(dest, '.scss'); 79 | const isCcaSassFile = util.isCcaSassFile(dest); 80 | // Force debug extension for Cca sass files because we can't update the references easily 81 | const ext = isCcaSassFile ? util.getThemeCssExtention('debug') : util.getThemeCssExtention(buildType); 82 | let pcssDest = util.destPath(path.join(path.dirname(dest), `${name}${ext}`)); 83 | if (isCcaSassFile) { 84 | const { componentPath, subFolders } = util.getComponentPathFromThemingFileDest({ 85 | context, 86 | dest, 87 | isReleaseBuild 88 | }); 89 | pcssDest = util.destPath(path.join(componentPath, subFolders, `${name}${ext}`)); 90 | } 91 | return pcssDest; 92 | } 93 | 94 | function _writePcssResultToFile(options, result) { 95 | if (options.sourceMap) { 96 | fs.ensureFileSync(`${options.outFile}.map`); 97 | fs.outputFileSync(`${options.outFile}.map`, result.map); 98 | } 99 | fs.ensureFileSync(options.outFile); 100 | fs.outputFileSync(options.outFile, result.css); 101 | } 102 | 103 | function _writeCustomizedCss(postcss, gplugin, outFile, destFile, option) { 104 | return new Promise((resolve, reject) => { 105 | let processoption = {}; 106 | // set the from property to undefined to avoid postcss warning 107 | if (!option) { 108 | processoption = { 109 | parser: postcss, 110 | from: undefined 111 | }; 112 | } else { 113 | processoption = { 114 | processors: [ 115 | gplugin({ overrideBrowserslist: 'last 2 Edge major versions, last 2 chrome major version, last 2 firefox major version, firefox esr, ie 11, last 2 safari major version, last 2 ios major version' }) 116 | ], 117 | parser: postcss, 118 | from: undefined 119 | }; 120 | } 121 | postcss(gplugin).process(outFile, processoption) 122 | .then((result) => { 123 | fs.ensureFileSync(destFile); 124 | fs.outputFileSync(destFile, result.css); 125 | resolve(); 126 | }) 127 | .catch(err => reject(err)); 128 | }); 129 | } 130 | 131 | function _getPcssTaskPromise(options, context) { 132 | let sass; 133 | try { 134 | sass = util.requireLocalFirst('sass'); 135 | } catch (e) { 136 | try { 137 | // fallback 138 | sass = util.requireLocalFirst('node-sass'); 139 | } catch (e2) { 140 | util.log.error('sass (or node-sass) is not installed. To install sass, run: ojet add sass.'); 141 | } 142 | } 143 | const postcss = util.requireLocalFirst('postcss'); 144 | const pcssCalc = util.requireLocalFirst('postcss-calc'); 145 | const autoPrefix = util.requireLocalFirst('autoprefixer'); 146 | return new Promise((resolve, reject) => { 147 | sass.render(options, (err, result) => { 148 | if (err) { 149 | console.log(err); 150 | reject(err); 151 | } else { 152 | _writePcssResultToFile(options, result); 153 | console.log(`Pcss compile\n From => ${options.file}\n To => ${options.outFile} finished..`); 154 | 155 | // compile custom property css to variable converted to value css 156 | const css = util.readFileSync(options.outFile); 157 | new Promise((resolve, reject) => { // eslint-disable-line 158 | _writeCustomizedCss(postcss, pcssCalc, css, options.outFile); 159 | console.log(`Pcss calc compile\n To => ${options.outFile} finished..`); 160 | resolve(); 161 | }) 162 | .then(() => { 163 | const autoprefixvarscss = util.readFileSync(options.outFile); 164 | _writeCustomizedCss(postcss, autoPrefix, autoprefixvarscss, options.outFile, 'prefix'); 165 | console.log(`Pcss Autoprefixer compile\n To => ${options.outFile} finished..`); 166 | resolve(); 167 | }); 168 | resolve(context); 169 | } 170 | return true; 171 | }); 172 | }); 173 | } 174 | 175 | function _getPcssTasksFromFileList(theme, fileList, context, isCcaLiveReload) { 176 | const promiseList = []; 177 | const opts = context.opts; 178 | const buildType = context.buildType; 179 | const pcssOpts = _getPcssTaskOptions(buildType, opts.pcss.options); 180 | fileList.forEach((file) => { 181 | const src = file.src; 182 | const dest = _getPcssDest(context, buildType, file.dest, false); 183 | if (!src || path.basename(src)[0] === '_') { 184 | // do nothing if the file name starts with underscore 185 | } else if (isCcaLiveReload && !_matchChangedCca(src, theme)) { 186 | // do nothing if the SASS file is not in right CCA for livereload 187 | } else { 188 | const fileOpts = { 189 | file: src, 190 | outFile: dest 191 | }; 192 | const finalPcssOpts = Object.assign({}, pcssOpts, fileOpts); 193 | promiseList.push(_getPcssTaskPromise(finalPcssOpts, context)); 194 | if (buildType === 'release' && util.isCcaSassFile(file.dest)) { 195 | // We need a second pass to /min for Cca sass release mode 196 | const fileOpts2 = { 197 | file: src, 198 | outFile: _getPcssDest(context, buildType, file.dest, true) 199 | }; 200 | const finalPcssOpts2 = Object.assign({}, pcssOpts, fileOpts2); 201 | promiseList.push(_getPcssTaskPromise(finalPcssOpts2, context)); 202 | } 203 | } 204 | }); 205 | return promiseList; 206 | } 207 | 208 | function _matchChangedCca(filePath, changedCcaTheme) { 209 | return filePath.indexOf(changedCcaTheme) !== -1; 210 | } 211 | 212 | function _getPcssTaskOptions(buildType, defaultPcssOpts) { 213 | let pcssOpts; 214 | if (buildType === 'release') { 215 | pcssOpts = { 216 | outputStyle: 'compressed' 217 | }; 218 | } else { 219 | pcssOpts = { 220 | outputStyle: 'expanded' 221 | }; 222 | } 223 | return Object.assign({}, pcssOpts, defaultPcssOpts); 224 | } 225 | 226 | module.exports = { 227 | getPromises: context => _getPcssPromises(context) 228 | }; 229 | -------------------------------------------------------------------------------- /lib/publish.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const component = require('./scopes/component'); 15 | const CONSTANTS = require('./constants'); 16 | const pack = require('./scopes/pack'); 17 | const util = require('./util'); 18 | 19 | /** 20 | * # Switch for 'ojet.publish()' 21 | * 22 | * @public 23 | * @param {string} scope 24 | * @param {string} parameter 25 | * @param {Object} options 26 | * @returns {Promise} 27 | */ 28 | module.exports = function (scope, parameter, options) { 29 | switch (scope) { 30 | case (CONSTANTS.API_SCOPES.COMPONENT): 31 | return component.publish(parameter, options); 32 | case (CONSTANTS.API_SCOPES.PACK): 33 | return pack.publish(parameter, options); 34 | default: 35 | if (options && util.hasProperty(options, 'path')) { 36 | return component.publish(parameter, options); 37 | } 38 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.PUBLISH}() 'scope' parameter.`); 39 | return false; 40 | } 41 | }; 42 | -------------------------------------------------------------------------------- /lib/remove.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const component = require('./scopes/component'); 15 | const CONSTANTS = require('./constants'); 16 | const pack = require('./scopes/pack'); 17 | const util = require('./util'); 18 | 19 | /** 20 | * # Switch for 'ojet.remove()' 21 | * 22 | * @public 23 | * @param {string} scope 24 | * @param {Array} parameters 25 | * @param {boolean} isStrip 26 | * @param {Object} [options] 27 | * @returns {Promise} 28 | */ 29 | module.exports = function (scope, parameters, isStrip, options) { 30 | switch (scope) { 31 | case (CONSTANTS.API_SCOPES.COMPONENT): 32 | return component.remove(parameters, isStrip, options); 33 | case (CONSTANTS.API_SCOPES.PACK): 34 | return pack.remove(parameters, options); 35 | default: 36 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.REMOVE}() 'scope' parameter.`); 37 | return false; 38 | } 39 | }; 40 | -------------------------------------------------------------------------------- /lib/sass.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const path = require('path'); 10 | const util = require('./util'); 11 | const fs = require('fs-extra'); 12 | const CONSTANT = require('./constants'); 13 | 14 | function _writeSassResultToFile(options, result) { 15 | if (options.sourceMap) { 16 | fs.ensureFileSync(`${options.outFile}.map`); 17 | fs.outputFileSync(`${options.outFile}.map`, result.map); 18 | } 19 | fs.ensureFileSync(options.outFile); 20 | fs.outputFileSync(options.outFile, result.css); 21 | } 22 | 23 | function _getSassTaskPromise(options, context) { 24 | let sass; 25 | try { 26 | sass = util.requireLocalFirst('sass'); 27 | } catch (e) { 28 | try { 29 | // fall back to node-sass if sass not found 30 | sass = util.requireLocalFirst('node-sass'); 31 | } catch (e2) { 32 | util.log.error('sass (or node-sass) is not installed. To install sass, run: ojet add sass.'); 33 | } 34 | } 35 | return new Promise((resolve, reject) => { 36 | sass.render(options, (err, result) => { 37 | if (err) { 38 | console.log(err); 39 | reject(err); 40 | } else { 41 | _writeSassResultToFile(options, result); 42 | console.log(`sass compile\n from ${options.file}\n to ${options.outFile} finished..`); 43 | resolve(context); 44 | } 45 | }); 46 | }); 47 | } 48 | 49 | function _getSassDest(context, buildType, dest, isReleaseBuild) { 50 | const name = path.basename(dest, '.scss'); 51 | const isCcaSassFile = util.isCcaSassFile(dest); 52 | // Force debug extension for Cca sass files because we can't update the references easily 53 | const ext = isCcaSassFile ? util.getThemeCssExtention('debug') : util.getThemeCssExtention(buildType); 54 | let sassDest = util.destPath(path.join(path.dirname(dest), name + ext)); 55 | if (isCcaSassFile) { 56 | const { componentPath, subFolders } = util.getComponentPathFromThemingFileDest({ 57 | context, 58 | dest, 59 | isReleaseBuild 60 | }); 61 | sassDest = util.destPath(path.join(componentPath, subFolders, `${name}${ext}`)); 62 | } 63 | return sassDest; 64 | } 65 | 66 | function _getSassPromises(context) { 67 | // compile only the changed theme during livereload 68 | if (context.changedTheme && context.changedTheme.compile) { 69 | return _getSassTasks(context.changedTheme, context); 70 | } 71 | 72 | if (context.changedCcaTheme) { 73 | console.log(`CCA ${context.changedCcaTheme} Changed...`); 74 | return _getCcaSassTasks(context, true); 75 | } 76 | 77 | return _getDefaultThemeTasks(context); 78 | } 79 | 80 | function _getDefaultThemeTasks(context) { 81 | const opts = context.opts; 82 | // compile the default theme 83 | let taskArray = []; 84 | if (opts.theme.compile) { 85 | taskArray = taskArray.concat(_getSassTasks(opts.theme, context)); 86 | } 87 | 88 | // if svg is enabled, re-compile Alta 89 | if ((opts.svg) && opts.theme.name === CONSTANT.DEFAULT_THEME) { 90 | taskArray = taskArray.concat(_getAltaSassTasks(opts.theme, context)); 91 | } 92 | 93 | // compile additional multi themes 94 | if (opts.themes) { 95 | opts.themes.forEach((singleTheme) => { 96 | if (singleTheme.compile) { 97 | taskArray = taskArray.concat(_getSassTasks(singleTheme, context)); 98 | } 99 | }); 100 | } 101 | 102 | // compile cca 103 | if (opts.sassCompile) { 104 | taskArray = taskArray.concat(_getCcaSassTasks(context, false)); 105 | } 106 | 107 | return taskArray; 108 | } 109 | 110 | function _getCcaSassTasks(context, isCcaLiveReload) { 111 | const fileList = util.getFileList(context.buildType, _getCcaFileList(context.opts.sass.fileList)); 112 | return _getSassTasksFromFileList(context.changedCcaTheme, fileList, context, isCcaLiveReload); 113 | } 114 | 115 | function _getSassTasks(theme, context) { 116 | let fileList = _getThemeFileList(context.opts.sass.fileList); 117 | fileList = util.getFileList(context.buildType, fileList, { theme }, { theme }); 118 | return _getSassTasksFromFileList(theme, fileList, context, false); 119 | } 120 | 121 | function _getAltaSassTasks(theme, context) { 122 | context.opts.theme.directory = _getAltaSourcePath(context.platform); //eslint-disable-line 123 | let fileList = _getThemeFileList(context.opts.altasass.fileList); 124 | fileList = util.getFileList(context.buildType, fileList, { theme }, { theme }); 125 | return _getSassTasksFromFileList(theme, fileList, context, false); 126 | } 127 | 128 | function _getAltaSourcePath(platform) { 129 | return path.join(CONSTANT.PATH_TO_ORACLEJET, 'scss', util.mapToSourceSkinName(platform)); 130 | } 131 | 132 | function _getCcaFileList(fileList) { 133 | return fileList.filter(fileObj => util.isCcaSassFile(fileObj.cwd)); 134 | } 135 | 136 | function _getThemeFileList(fileList) { 137 | return fileList.filter(fileObj => !util.isCcaSassFile(fileObj.cwd)); 138 | } 139 | 140 | function _getSassTasksFromFileList(theme, fileList, context, isCcaLiveReload) { 141 | const promiseList = []; 142 | const opts = context.opts; 143 | const buildType = context.buildType; 144 | const sassOpts = _getSassTaskOptions(buildType, opts.sass.options); 145 | fileList.forEach((file) => { 146 | const src = file.src; 147 | const dest = _getSassDest(context, buildType, file.dest, false); 148 | if (!src || path.basename(src)[0] === '_') { 149 | // do nothing if the file name starts with underscore 150 | } else if (isCcaLiveReload && !_matchChangedCca(src, theme)) { 151 | // do nothing if the SASS file is not in right CCA for livereload 152 | } else { 153 | const fileOpts = { 154 | file: src, 155 | outFile: dest 156 | }; 157 | const finalSassOpts = Object.assign({}, sassOpts, fileOpts); 158 | promiseList.push(_getSassTaskPromise(finalSassOpts, context)); 159 | if (buildType === 'release' && util.isCcaSassFile(file.dest)) { 160 | // We need a second pass to /min for Cca sass release mode 161 | const fileOpts2 = { 162 | file: src, 163 | outFile: _getSassDest(context, buildType, file.dest, true) 164 | }; 165 | const finalSassOpts2 = Object.assign({}, sassOpts, fileOpts2); 166 | promiseList.push(_getSassTaskPromise(finalSassOpts2, context)); 167 | } 168 | } 169 | }); 170 | return promiseList; 171 | } 172 | 173 | function _getSassTaskOptions(buildType, defaultSassOpts) { 174 | let sassOpts; 175 | if (buildType === 'release') { 176 | sassOpts = { 177 | outputStyle: 'compressed' 178 | }; 179 | } else { 180 | sassOpts = { 181 | outputStyle: 'expanded' 182 | }; 183 | } 184 | 185 | return Object.assign({}, sassOpts, defaultSassOpts); 186 | } 187 | 188 | function _matchChangedCca(filePath, changedCcaTheme) { 189 | return filePath.indexOf(changedCcaTheme) !== -1; 190 | } 191 | 192 | module.exports = { 193 | getPromises: context => _getSassPromises(context) 194 | }; 195 | -------------------------------------------------------------------------------- /lib/scopes/exchange.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | // Oracle 15 | const CONSTANTS = require('../constants'); 16 | const exchangeUtils = require('../utils.exchange'); 17 | const util = require('../util'); 18 | 19 | /** 20 | * # Exchange 21 | * 22 | * @public 23 | */ 24 | const exchange = module.exports; 25 | 26 | /** 27 | * ## configure 28 | * 29 | * @public 30 | * @param {string} url - exchange url 31 | */ 32 | exchange.configureUrl = function (url) { 33 | const configObj = util.getOraclejetConfigJson(); 34 | configObj[CONSTANTS.EXCHANGE_URL_PARAM] = url; 35 | // Because a new url has been set, delete the 'cached' local components support flag. 36 | delete configObj[CONSTANTS.EXCHANGE_LOCAL_COMPONENTS_SUPPORT]; 37 | try { 38 | util.writeFileSync(`./${CONSTANTS.ORACLE_JET_CONFIG_JSON}`, JSON.stringify(configObj, null, 2)); 39 | util.log.success(`Exchange url set: ${url}`); 40 | return Promise.resolve(); 41 | } catch (e) { 42 | util.log.error(`Exchange url could not be set. ${e}`, true); 43 | return Promise.reject(); 44 | } 45 | }; 46 | 47 | /** 48 | * ## search 49 | * 50 | * @public 51 | * @param {string} parameter 52 | * @param {Object} options 53 | * @returns {Promise} 54 | */ 55 | exchange.search = function (parameter, options) { 56 | return new Promise((resolve, reject) => { 57 | util.ensureParameters(parameter, CONSTANTS.API_TASKS.SEARCH); 58 | util.log(`Searching for '${parameter}' in the Exchange ...`); 59 | 60 | // The first level function stores user input for the session 61 | process.env.options = JSON.stringify(options); 62 | 63 | util.loginIfCredentialsProvided() 64 | .then(() => { // eslint-disable-line 65 | return util.request({ 66 | path: `/components/?q=${parameter}*&format=compact&extraFields=tags&componentFields=displayName,description`, 67 | }) 68 | .then((responseData) => { 69 | const response = responseData.response; 70 | const responseBody = responseData.responseBody; 71 | return exchangeUtils.validateAuthenticationOfRequest( 72 | response, 73 | () => { return this.search(parameter, options); }, // eslint-disable-line 74 | () => { 75 | util.checkForHttpErrors(response, responseBody); 76 | 77 | const components = JSON.parse(responseBody).items; 78 | if (components.length === 0) { 79 | util.log.success('No components found.'); 80 | } else { 81 | _customisePrintOutput(options); 82 | _printHead(); 83 | _printResults(components, parameter); 84 | } 85 | } 86 | ); 87 | }); 88 | }) 89 | .then(() => { 90 | resolve(); 91 | }) 92 | .catch((error) => { 93 | reject(); 94 | util.log.error(error, true); 95 | }); 96 | }); 97 | }; 98 | 99 | // When modifying, make sure the search url includes required fields 100 | const table = { 101 | fullName: 40, 102 | displayName: 40, 103 | tags: 40, 104 | description: 40 105 | }; 106 | 107 | const space = ' '; 108 | 109 | /** 110 | * ## _customisePrintOutput 111 | * 112 | * @private 113 | * @param {Object} options 114 | */ 115 | 116 | function _customisePrintOutput(options) { 117 | // Versions 118 | if (options.versions) { 119 | // Do not show tags and description 120 | delete table.tags; 121 | delete table.description; 122 | 123 | // Use this space to show all available versions 124 | table.versions = 80; 125 | } 126 | } 127 | 128 | /** 129 | * ## _printHead 130 | * 131 | * @private 132 | */ 133 | function _printHead() { 134 | let headLine = ''; 135 | Object.keys(table).forEach((key) => { 136 | const colSpaces = table[key] - key.length; 137 | if (colSpaces < 0) { 138 | headLine += `<${key.substring(0, table[key] - 2)}>${space}`; 139 | } else { 140 | headLine += `<${key}>${space.repeat(colSpaces - 2)}${space}`; 141 | } 142 | }); 143 | util.log(headLine); 144 | } 145 | 146 | /** 147 | * ## _printResults 148 | * 149 | * @private 150 | * @param {Array} components 151 | * @param {string} parameter 152 | */ 153 | function _printResults(components, parameter) { 154 | components.forEach((component) => { 155 | const comp = component; 156 | let line = ''; 157 | 158 | Object.keys(table).forEach((key) => { 159 | // 'displayName' and 'description' are within metadata[component] scope 160 | if (['displayName', 'description'].indexOf(key) > -1) { 161 | comp.component = comp.component || {}; 162 | comp[key] = comp.component[key] || ''; 163 | } 164 | 165 | if (util.hasProperty(comp, key)) { 166 | // Custom handling for 'tags' 167 | if (key === 'tags') { 168 | comp[key] = _processTags(comp[key], parameter); 169 | } 170 | 171 | const colSpaces = table[key] - comp[key].length; 172 | 173 | if (colSpaces < 0) { 174 | line += comp[key].substring(0, table[key]) + space; 175 | } else { 176 | line += comp[key] + space.repeat(colSpaces) + space; 177 | } 178 | } 179 | }); 180 | 181 | util.log(line); 182 | }); 183 | } 184 | 185 | /** 186 | * ## _processTags 187 | * 188 | * @private 189 | * @param {Array} tags 190 | * @param {string} parameter 191 | */ 192 | function _processTags(tags, parameter) { 193 | const lowerCaseTags = tags.map(value => value.toLowerCase()); 194 | 195 | function matchTag(tag) { 196 | return tag.match(parameter.toLowerCase()); 197 | } 198 | 199 | const i = lowerCaseTags.findIndex(matchTag); 200 | 201 | return i > -1 ? tags[i] : tags[0] || ''; 202 | } 203 | -------------------------------------------------------------------------------- /lib/search.js: -------------------------------------------------------------------------------- 1 | #! /usr/bin/env node 2 | /** 3 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 4 | Licensed under The Universal Permissive License (UPL), Version 1.0 5 | as shown at https://oss.oracle.com/licenses/upl/ 6 | 7 | */ 8 | 9 | 'use strict'; 10 | 11 | /** 12 | * ## Dependencies 13 | */ 14 | const exchange = require('./scopes/exchange'); 15 | const CONSTANTS = require('./constants'); 16 | const util = require('./util'); 17 | 18 | /** 19 | * # Switch for 'ojet.search()' 20 | * 21 | * @public 22 | * @param {string} scope 23 | * @param {string} parameter 24 | * @param {Object} options 25 | * @returns {Promise} 26 | */ 27 | module.exports = function (scope, parameter, options) { 28 | switch (scope) { 29 | case (CONSTANTS.API_SCOPES.EXCHANGE): 30 | return exchange.search(parameter, options); 31 | default: 32 | util.log.error(`Please specify ojet.${CONSTANTS.API_TASKS.SEARCH}() 'scope' parameter.`); 33 | return false; 34 | } 35 | }; 36 | -------------------------------------------------------------------------------- /lib/serve.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * # Dependencies 11 | */ 12 | /* Node.js native */ 13 | /* Oracle */ 14 | const config = require('./config'); 15 | 16 | const ojetBuild = require('./build'); 17 | const serveWeb = require('./serveWeb'); 18 | const serveWebpack = require('./webpack/serve'); 19 | const util = require('./util'); 20 | const valid = require('./validations'); 21 | 22 | /** 23 | * # Serve API 24 | * ## ojet.serve([platform], [options]); 25 | * 26 | * Usage example 27 | * 28 | * ``` 29 | * const ojet = require('oraclejet-toooling'); 30 | * 31 | * ojet.serve('ios', { 32 | * livereload: false, 33 | * server-port: 12345 34 | * }); 35 | * ``` 36 | * 37 | * @public 38 | * @param {string} [platform='web'] - Platform, defaults to 'web' 39 | * @param {Object} [options] - Options object 40 | * @param {boolean} [options.build=true] - Build before serve 41 | * @param {string} [options.buildType] - Build type: debug/release 42 | * @param {string} [options.buildConfig] - Path to configuration file 43 | * @param {Object} [options.connect] - Subtask connect config object 44 | * @param {string} [options.destination] - Deploy to device/emulator/target/browser 45 | * @param {string} [options.destinationTarget]- Specific destination 46 | * @param {boolean} [options.livereload=true] - Livereload 47 | * @param {number} [options.livereloadPort] - Livereload port number 48 | * @param {number} [options.port] - Content server port number 49 | * @param {boolean} [options.sassCompile] - Sass compile and watch 50 | * @param {string} [options.theme] - Theme option, default is 'alta' 51 | * @param {Array} [options.themes] - Themes option, default to first element defined 52 | * @param {Object} [options.watch] - Subtask watch config object 53 | * @returns {Promise} 54 | */ 55 | const serve = module.exports; 56 | module.exports = function (platform, options) { 57 | if (util.buildWithWebpack()) { 58 | return serveWebpack(options); 59 | } 60 | let platformArg = platform; 61 | let optionsArg = options; 62 | /* Initial arguments check */ 63 | if (serve.length === 1) { 64 | if (typeof platformArg === 'object') { 65 | optionsArg = platform; 66 | platformArg = undefined; 67 | } 68 | } 69 | /* Wrapped in promise to make ojet.serve() thenable */ 70 | return new Promise(() => { 71 | config.loadOraclejetConfig(platform); 72 | const validPlatform = valid.platform(platformArg); 73 | const validDefaultServeConfig = valid.getDefaultServeConfig(); 74 | config.set('defaultServeConfig', validDefaultServeConfig); 75 | /* Validate entries and/or set defaults */ 76 | const validOptions = _validateOptions(optionsArg, validPlatform); 77 | /* Update config */ 78 | config.set('platform', validPlatform); 79 | config.set('serve', validOptions); 80 | console.log(`Build: ${validOptions.build}`); 81 | console.log(`BuildType: ${validOptions.buildType}`); 82 | console.log(`Destination: ${validOptions.destination}`); 83 | console.log(`Destination target: ${validOptions.destinationTarget}`); 84 | console.log(`Livereload: ${validOptions.livereload}`); 85 | console.log(`Watch files: ${validOptions.watchFiles}`); 86 | if (validOptions.livereloadPort) { 87 | console.log(`Livereload port: ${validOptions.livereloadPort}`); 88 | } 89 | console.log(`Platform: ${validPlatform}`); 90 | if (validPlatform === 'web') { 91 | console.log(`Port: ${validOptions.connect.options.port}`); 92 | } 93 | if (validOptions.destination === 'browser' && validOptions.port) { 94 | console.log(`Port: ${validOptions.port}`); 95 | } 96 | console.log(`Theme: ${validOptions.theme.name}`); 97 | console.log(`Theme platform: ${validOptions.theme.platform}`); 98 | console.log(`Theme version: ${validOptions.theme.version}`); 99 | /* Should build before serve? */ 100 | const build = _getBuildAction(); 101 | 102 | return serveWeb(build); 103 | }); 104 | }; 105 | 106 | /** 107 | * ## _validateOptions 108 | * Validate options and set the defaults 109 | * 110 | * @private 111 | * @param {Object} [options] - Options object 112 | * @param {boolean} [options.build=true] - Build before serve 113 | * @param {string} [options.buildType] - Build type: debug/release 114 | * @param {string} [options.buildConfig] - Path to configuration file 115 | * @param {string} [options.destination] - Deploy to device/emulator 116 | * @param {string} [options.destinationTarget] - Deploy to specific destination 117 | * @param {boolean} [options.livereload=true] - Livereload 118 | * @param {number} [options.livereloadPort] - Livereload port number 119 | * @param {number} [options.port] - Content server port number 120 | * @param {string} [validPlatform] - Valid platform 121 | * @returns {Object} options - Valid options 122 | */ 123 | function _validateOptions(options, validPlatform) { 124 | let validOptions = util.cloneObject(options); 125 | validOptions.build = _validateBuild(validOptions.build); 126 | validOptions.buildType = valid.buildType(validOptions); 127 | validOptions = valid.theme(validOptions, validPlatform); 128 | validOptions = _validateLivereload(validOptions); 129 | validOptions = _validateWatchFiles(validOptions); 130 | validOptions.serverUrl = _validateServeUrl(validOptions); 131 | validOptions.port = _validatePorts(validOptions.port, 'server-port'); 132 | validOptions.livereloadPort = _validatePorts(validOptions.livereloadPort, 'livereload-port'); 133 | return _mergeServeWatchConnectOptions(validOptions, validPlatform); 134 | } 135 | 136 | function _mergeServeWatchConnectOptions(options, platform) { 137 | let mergedOptions = options || {}; 138 | const DEFAULT_CONFIG = config('defaultServeConfig'); 139 | mergedOptions = util.mergePlatformOptions(mergedOptions, platform); 140 | mergedOptions.connect = _getConnectOptions(mergedOptions, DEFAULT_CONFIG.connect); 141 | mergedOptions.watch = _getWatchOptions(mergedOptions, DEFAULT_CONFIG.watch); 142 | return mergedOptions; 143 | } 144 | 145 | function _getConnectOptions(opts, defaultOpts) { 146 | const connectConfig = util.mergeDefaultOptions(opts.connect, defaultOpts); 147 | const livereload = opts.livereload ? opts.livereloadPort : false; 148 | let open = opts.connect.open === undefined ? connectConfig.options.open : opts.connect.open; 149 | open = opts.destination === 'server-only' ? false : open; 150 | const port = opts.port || connectConfig.options.port; 151 | return _overrideConnectOptions(connectConfig, { livereload, open, port }); 152 | } 153 | 154 | function _overrideConnectOptions(configParam, overrides) { 155 | // Connect options structure differs from that of watch. 156 | // Use object assign instead of util.mergeDefaultOptions to merge 157 | const connectOpts = Object.assign({}, configParam.options, overrides); 158 | return { options: connectOpts }; 159 | } 160 | 161 | function _getWatchOptions(opts, defaultOpts) { 162 | const watchConfig = util.mergeDefaultOptions(opts.watch, defaultOpts); 163 | const livereload = opts.livereload ? opts.livereloadPort : false; 164 | return _overrideOptionsForConfigs(watchConfig, { livereload }); 165 | } 166 | 167 | function _overrideOptionsForConfigs(configParam, overrides) { 168 | Object.keys(configParam).forEach((configKey) => { 169 | const subConfig = configParam[configKey]; 170 | Object.keys(overrides).forEach((overrideKey) => { 171 | if (overrides[overrideKey] !== undefined) { 172 | subConfig.options[overrideKey] = overrides[overrideKey]; 173 | } 174 | }); 175 | }); 176 | return configParam; 177 | } 178 | 179 | /** 180 | * ## _validateBuild 181 | * 182 | * @private 183 | * @param {boolean} build 184 | * @returns {boolean} validBuild 185 | */ 186 | function _validateBuild(build) { 187 | let validBuild = build; 188 | if (validBuild === 'false') return false; 189 | if (validBuild === 'true') return true; 190 | if (validBuild || validBuild === false) { 191 | util.validateType('build', build, 'boolean'); 192 | } else { 193 | const defaultBuild = config('defaultServeConfig').options.build; 194 | util.validateType('build default config', defaultBuild, 'boolean'); 195 | validBuild = defaultBuild; 196 | } 197 | return validBuild; 198 | } 199 | 200 | /** 201 | * ## _validateLivereload 202 | * 203 | * @private 204 | * @param {Object} options 205 | * @returns {Object} options 206 | */ 207 | function _validateLivereload(options) { 208 | if (options.livereload || options.livereload === false) { 209 | util.validateType('livereload', options.livereload, 'boolean'); 210 | } else { 211 | util.validateType('livereload default config', config('defaultServeConfig').options.livereload, 'boolean'); 212 | } 213 | 214 | return _applyLivereloadConditions(options); 215 | } 216 | 217 | /** 218 | * ## _validateWatchFiles 219 | * 220 | * @private 221 | * @param {Object} options 222 | * @returns {Object} options 223 | */ 224 | function _validateWatchFiles(options) { 225 | if (options.watchFiles || options.watchFiles === false) { 226 | util.validateType('watchFiles', options.watchFiles, 'boolean'); 227 | } else { 228 | util.validateType('watch default config', config('defaultServeConfig').options.watchFiles, 'boolean'); 229 | } 230 | 231 | return _applyWatchFileConditions(options); 232 | } 233 | 234 | /** 235 | * ## _validateServerUrl 236 | * 237 | * @private 238 | * @param {Object} options 239 | * @returns {undefined || string} server url 240 | */ 241 | function _validateServeUrl(options) { 242 | const serverUrl = options['server-url']; 243 | if (serverUrl) { 244 | util.validateType('server-url', serverUrl, 'string'); 245 | } 246 | return serverUrl; 247 | } 248 | 249 | /** 250 | * ## _validatePorts 251 | * 252 | * @private 253 | * @param {String} port 254 | * @param {String} portType - 'livereload-port' vs. 'server-port' 255 | * @returns {undefined || number} validPort 256 | */ 257 | function _validatePorts(port, portType) { 258 | let validPort; 259 | if (port !== undefined) { 260 | if (isNaN(port)) { 261 | util.validateType(portType, port, 'number'); 262 | } else { 263 | validPort = parseInt(port, 10); 264 | } 265 | } 266 | return validPort; 267 | } 268 | 269 | /** 270 | * ## _applyLivereloadConditions 271 | * Applies business logic to provided options object, sets default 272 | * 273 | * @private 274 | * @param {Object} options 275 | * @returns {Object} validOptions 276 | */ 277 | function _applyLivereloadConditions(options) { 278 | const validOptions = util.cloneObject(options); 279 | /* 1. Turn off livereload if release build is on */ 280 | if (validOptions.buildType === 'release' && !validOptions.livereload) { 281 | util.log.warning('Livereload can\'t be used for release mode. Turning it off.'); 282 | validOptions.livereload = false; 283 | } 284 | 285 | /* 2. Not release and livereload not defined, use the default value */ 286 | if (validOptions.buildType !== 'release' && !validOptions.livereload && validOptions.livereload !== false) { 287 | validOptions.livereload = config('defaultServeConfig').options.livereload; 288 | validOptions.livereloadPort = options.livereloadPort || config('defaultServeConfig').options.livereloadPort; 289 | } 290 | 291 | /* 3. Release build can't be livereloaded */ 292 | if (validOptions.buildType === 'release' && validOptions.livereload === true) { 293 | util.log.error('Livereload can\'t be used for release build'); 294 | } 295 | 296 | return validOptions; 297 | } 298 | 299 | /** 300 | * ## _applyLivereloadConditions 301 | * Applies business logic to provided options object, sets default 302 | * 303 | * @private 304 | * @param {Object} options 305 | * @returns {Object} validOptions 306 | */ 307 | function _applyWatchFileConditions(options) { 308 | const validOptions = util.cloneObject(options); 309 | /* 1. Turn off watch if release build is on */ 310 | if (validOptions.buildType === 'release' && !validOptions.watchFiles) { 311 | util.log.warning('Watch files can\'t be used for release mode. Turning it off.'); 312 | validOptions.watchFiles = false; 313 | } 314 | 315 | /* 2. Not release and watch not defined, use the default value */ 316 | if (validOptions.buildType !== 'release' && !validOptions.watchFiles && validOptions.watchFiles !== false) { 317 | validOptions.watchFiles = config('defaultServeConfig').options.watchFiles; 318 | } 319 | 320 | /* 3. Release build can't be watched */ 321 | if (validOptions.buildType === 'release' && validOptions.watchFiles === true) { 322 | util.log.error('Watch files can\'t be used for release build'); 323 | } 324 | 325 | return validOptions; 326 | } 327 | 328 | /** 329 | * ## _getBuildAction 330 | * Calling ojet.build or just noop function to start promise chain 331 | * 332 | * @private 333 | * @returns {function} - Build action 334 | */ 335 | function _getBuildAction() { 336 | if (config.get('serve').build) { 337 | /* Build */ 338 | return () => { 339 | util.log('Building app.'); 340 | const serveConfig = config.get('serve'); 341 | const buildConfig = serveConfig.buildOptions; 342 | Object.assign(buildConfig, { 343 | ...serveConfig, 344 | buildForServe: true 345 | }); 346 | return ojetBuild(config.get('platform'), buildConfig); 347 | }; 348 | } 349 | /* Do Not build - noop function to start promise chain */ 350 | return () => new Promise((res) => { 351 | util.log('Skipping build...'); 352 | res(); 353 | }); 354 | } 355 | -------------------------------------------------------------------------------- /lib/serve/connect.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * # Dependencies 11 | */ 12 | 13 | /* 3rd party */ 14 | const express = require('express'); 15 | const serveStatic = require('serve-static'); 16 | const serveIndex = require('serve-index'); 17 | const http = require('http'); 18 | const injectLiveReload = require('connect-livereload'); 19 | const open = require('open'); 20 | 21 | const utils = require('../util'); 22 | 23 | /** 24 | * # serve Connect Module 25 | * 26 | * @public 27 | */ 28 | module.exports = function (opts, context) { 29 | utils.log('Starting web server.'); 30 | const connectOpts = _processCustomOptions(opts); 31 | return new Promise((resolve, reject) => { 32 | const app = context.express ? context.express : express(); 33 | 34 | const defaultMiddleware = _getMiddleware(connectOpts); 35 | const customMiddleware = Array.isArray(context.middleware) ? context.middleware : null; 36 | const customPreMiddleware = context.preMiddleware || []; 37 | const customPostMiddleware = context.postMiddleware || []; 38 | 39 | let appMiddleware; 40 | if (customMiddleware) { 41 | appMiddleware = customMiddleware; 42 | } else { 43 | appMiddleware = [...customPreMiddleware, ...defaultMiddleware, ...customPostMiddleware]; 44 | } 45 | 46 | appMiddleware.forEach((middleware) => { 47 | let middlewareArray = middleware; 48 | if (!Array.isArray(middlewareArray)) { 49 | middlewareArray = [middlewareArray]; 50 | } 51 | app.use.apply(app, middlewareArray); //eslint-disable-line 52 | }); 53 | 54 | let server; 55 | if (context.server) { 56 | // Use user's custom server -- this means none of our default middleware applies 57 | server = context.server; 58 | } else if (context.serverOptions) { 59 | server = context.http ? context.http.createServer(context.serverOptions, app) : 60 | http.createServer(context.serverOptions, app); 61 | } else { 62 | server = context.http ? context.http.createServer(app) : http.createServer(app); 63 | } 64 | 65 | const hostname = connectOpts.hostname || '0.0.0.0'; 66 | const targetHostname = hostname === '0.0.0.0' ? 'localhost' : hostname; 67 | const urlPrefix = context.urlPrefix ? context.urlPrefix : 'http'; 68 | const defaultTarget = `${urlPrefix}://${targetHostname}:${connectOpts.port}`; 69 | const target = connectOpts.serverUrl ? connectOpts.serverUrl : defaultTarget; 70 | console.log(`Connecting to ${target}`); 71 | server 72 | .listen(connectOpts.port, connectOpts.hostname) 73 | .on('listening', () => { 74 | utils.log.success(`Server ready: ${target}`); 75 | if (connectOpts.open) { 76 | open(target); 77 | } 78 | resolve(); 79 | }) 80 | .on('error', (err) => { 81 | reject(err); 82 | }); 83 | }); 84 | }; 85 | 86 | function _getMiddleware(options) { 87 | let middlewares = []; 88 | middlewares = _getDefaultMiddleware(options); 89 | if (options.livereload) { 90 | const livereloadConfig = { port: options.livereloadPort, hostname: options.hostname }; 91 | middlewares.unshift(injectLiveReload(livereloadConfig)); 92 | } 93 | return middlewares; 94 | } 95 | 96 | function _getDefaultMiddleware(options) { 97 | const middlewares = []; 98 | const middlewareOpts = options; 99 | if (!Array.isArray(middlewareOpts.base)) { 100 | middlewareOpts.base = [middlewareOpts.base]; 101 | } 102 | // Options for serve-static module. See https://www.npmjs.com/package/serve-static 103 | const defaultStaticOptions = {}; 104 | const directory = middlewareOpts.directory || middlewareOpts.base[middlewareOpts.base.length - 1]; 105 | middlewareOpts.base.forEach((base) => { 106 | // Serve static files. 107 | const rootPath = base.path || base; 108 | const staticOptions = base.options || defaultStaticOptions; 109 | middlewares.push(serveStatic(rootPath, staticOptions)); 110 | }); 111 | // Make directory browse-able. 112 | middlewares.push(serveIndex(directory.path || directory)); 113 | return middlewares; 114 | } 115 | 116 | function _processCustomOptions(opts) { 117 | const result = opts || null; 118 | if (opts.hostname === '*') { 119 | result.hostname = ''; 120 | } 121 | 122 | if (opts.port === '?') { 123 | result.port = 0; 124 | } 125 | 126 | const defaultOpts = _getDefaultOptions(); 127 | return Object.assign({ livereloadPort: opts.livereloadPort }, defaultOpts, result); 128 | } 129 | 130 | function _getDefaultOptions() { 131 | return { 132 | port: 8000, 133 | hostname: '0.0.0.0', 134 | livereload: true, 135 | open: true 136 | }; 137 | } 138 | -------------------------------------------------------------------------------- /lib/serve/watch.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * # Dependencies 11 | */ 12 | const path = require('path'); 13 | const chokidar = require('chokidar'); 14 | const tinylr = require('tiny-lr'); 15 | 16 | const watchall = { 17 | running: false 18 | }; 19 | const watchers = {}; 20 | 21 | const buildCommon = require('../buildCommon'); 22 | const serveWebFileChangeHandler = require('../serveWebFileChangeHandler'); 23 | const util = require('../util'); 24 | const config = require('../config'); 25 | const valid = require('../validations'); 26 | const CONSTANTS = require('../constants'); 27 | const hookRunner = require('../hookRunner'); 28 | 29 | /** 30 | * # serve Watch Module 31 | * 32 | * @public 33 | */ 34 | module.exports = function (opts, livereloadPort, context) { 35 | _modifySourceFilesGlobPattern(opts); 36 | const watchOpts = _addSrcOverride(opts); 37 | const watchFiles = context.serveOpts.watchFiles; 38 | return new Promise((resolve, reject) => { 39 | if (watchall.running === false) { 40 | _startLiveReloadServer(watchOpts, livereloadPort, context) 41 | .then(() => { 42 | if (watchFiles) { 43 | util.log('Starting watcher.'); 44 | _startWatchers(opts, context); 45 | } 46 | }) 47 | .then(() => { 48 | if (watchFiles) { 49 | watchall.running = true; 50 | } 51 | resolve(); 52 | }) 53 | .catch((err) => { 54 | reject(err); 55 | }); 56 | } else { 57 | resolve(); 58 | } 59 | }); 60 | }; 61 | 62 | function _modifySourceFilesGlobPattern(opts) { 63 | const sourceAttribute = Object.getOwnPropertyNames(opts); 64 | sourceAttribute.forEach((attribute) => { 65 | _modifyFilePathGlobPattern(attribute, opts); 66 | }); 67 | } 68 | 69 | // The glob pattern follow the js/ts file src folder format with js/ts subfolders. 70 | // Vdom apps do not have the js/ts folder(s); therefore, there is an additional /./ 71 | // in the glob pattern src/./**/*., which seems to make the add event not 72 | // fire. Removing the /./ ensures the added event fires. 73 | function _modifyFilePathGlobPattern(attribute, opts) { 74 | opts[attribute].files.forEach((file, index) => { 75 | // eslint-disable-next-line no-param-reassign 76 | opts[attribute].files[index] = file.replace('src/./', 'src/'); 77 | }); 78 | } 79 | 80 | function _addSrcOverride(opts) { 81 | const result = opts; 82 | const srcOverrideDir = config('paths').src.web; 83 | result.sourceFiles.files.push(`${srcOverrideDir}/**/*`); 84 | return result; 85 | } 86 | 87 | function _startLiveReloadServer(opts, port, context) { 88 | return new Promise((resolve, reject) => { 89 | const livereload = context.serveOpts.livereload; 90 | const server = context.liveReloadServer ? context.liveReloadServer : tinylr({ port }); 91 | if (server && server.listen && livereload) { 92 | // Only listen if listen is available and livereload not disabled. 93 | server.listen(port, (err) => { 94 | if (err) { 95 | reject(err); 96 | } 97 | util.log(`Listening on port ${port}.`); 98 | watchall.server = server; 99 | resolve(opts); 100 | }); 101 | } else { 102 | resolve(opts); 103 | } 104 | }); 105 | } 106 | 107 | function _startWatchers(opts, context) { 108 | util.log('Watching files.'); 109 | const userOptions = context.userOptions; 110 | const intervalValue = util.getOraclejetConfigJson().watchInterval; 111 | util.log(`Watching Interval: ${intervalValue}.`); 112 | return new Promise((resolve, reject) => { 113 | try { 114 | Object.keys(opts).forEach((watchTarget) => { 115 | // eslint-disable-next-line max-len 116 | watchers[watchTarget] = _startWatcher(watchTarget, intervalValue, opts, userOptions); 117 | }); 118 | resolve(); 119 | } catch (err) { 120 | reject(err); 121 | } 122 | }); 123 | } 124 | 125 | function _startWatcher(target, intervalValue, opts, userOptions) { 126 | const targetFiles = opts[target].files; 127 | const watcher = chokidar.watch(targetFiles, 128 | { usePolling: true, interval: intervalValue, ignoreInitial: true }); 129 | 130 | // Using watcher.on(...) seems to have some issues; hence, the use of watcher.once(...). 131 | // See: https://github.com/paulmillr/chokidar/issues/286#issuecomment-94285269. 132 | watcher.once('ready', () => { 133 | util.log(`Watcher: ${target} is ready.`); 134 | }); 135 | 136 | watcher.on('change', (file) => { 137 | util.log(`Changed: ${file}`); 138 | const watcherContext = {}; 139 | watcherContext.watcher = { action: 'changed', file }; 140 | watcherContext.userOptions = userOptions; 141 | hookRunner('before_watch', watcherContext); 142 | _defaultFileChangeHandler({ opts, target, filePath: file }) 143 | .then(_runCustomPostWatchPromise) 144 | .then(() => { 145 | if (watchall.server) { 146 | watchall.server.changed({ body: { files: [file] } }); 147 | } 148 | hookRunner('after_watch', watcherContext); 149 | util.log('Page reloaded resume watching.'); 150 | }) 151 | .catch((err) => { 152 | util.log.error(err); 153 | }); 154 | }); 155 | 156 | watcher.on('add', (file) => { 157 | util.log(`Added: ${file}`); 158 | const watcherContext = {}; 159 | watcherContext.watcher = { action: 'added', file }; 160 | hookRunner('before_watch', watcherContext); 161 | _defaultFileChangeHandler({ opts, target, filePath: file }) 162 | .then(() => { 163 | watchall.server.changed({ body: { files: [file] } }); 164 | hookRunner('after_watch', watcherContext); 165 | util.log('Page reloaded resume watching.'); 166 | }) 167 | .catch((err) => { 168 | util.log.error(err); 169 | }); 170 | }); 171 | 172 | return watcher; 173 | } 174 | 175 | function _defaultFileChangeHandler(context) { 176 | const buildContext = _getBuildContext(); 177 | const filePath = context.filePath; 178 | 179 | return new Promise((resolve, reject) => { 180 | try { 181 | if (_isThemeFile(filePath)) { 182 | if (util.isCcaSassFile(filePath)) { 183 | config('changedCcaTheme', _getCcaPath(filePath)); 184 | config('changedTheme', null); 185 | } else { 186 | config('changedTheme', _getThemeObjFromPath(filePath)); 187 | config('changedCcaTheme', null); 188 | } 189 | resolve(context); 190 | } else if (config.get('platform') === 'web') { 191 | serveWebFileChangeHandler(filePath, buildContext).then(() => { 192 | resolve(context); 193 | }); 194 | } 195 | } catch (err) { 196 | reject(err); 197 | } 198 | }); 199 | } 200 | 201 | function _runCustomPostWatchPromise(context) { 202 | // programmatically build a promise chain to run all custom commands in sequence 203 | let commandSequence = Promise.resolve(); 204 | const customCommands = context.opts[context.target].commands; 205 | if (!customCommands) { 206 | return commandSequence; 207 | } 208 | util.log(`Trigggering commands ${customCommands}.`); 209 | customCommands.forEach((command) => { 210 | const commandPromise = _getCommandPromise(command); 211 | commandSequence = commandSequence.then(() => commandPromise) 212 | .then(() => { 213 | util.log(`Command ${command} completed..`); 214 | }); 215 | }); 216 | return commandSequence; 217 | } 218 | 219 | function _getCommandPromise(command) { 220 | if (command === 'compileSass') { 221 | return _getCompileSassPromise(); 222 | } 223 | 224 | if (command === 'copyThemes') { 225 | return _getCopyThemePromise(); 226 | } 227 | 228 | return util.exec(command); 229 | } 230 | 231 | function _getCompileSassPromise() { 232 | // Skip sass compile tasks if the global sass compile is disabled 233 | if (config('serve').sassCompile === false) { 234 | return Promise.resolve(); 235 | } 236 | 237 | const context = _getBuildContext(); 238 | context.changedTheme = _getThemeObjFromName(config('changedTheme'), context.opts); 239 | context.changedCcaTheme = config('changedCcaTheme'); 240 | return new Promise((resolve, reject) => { 241 | buildCommon.css(context) 242 | .then(buildCommon.copyThemes) 243 | .then(() => { 244 | resolve(); 245 | }) 246 | .catch(err => reject(err)); 247 | }); 248 | } 249 | 250 | function _getCopyThemePromise() { 251 | const context = _getBuildContext(); 252 | context.changedTheme = _getThemeObjFromName(config('changedTheme'), context.opts); 253 | return new Promise((resolve, reject) => { 254 | buildCommon.copyThemes(context) 255 | .then(() => { 256 | resolve(); 257 | }) 258 | .catch(err => reject(err)); 259 | }); 260 | } 261 | 262 | function _isThemeFile(filePath) { 263 | const srcThemes = new RegExp(config('paths').src.themes); 264 | const stagingThemes = new RegExp(config('paths').staging.themes); 265 | const srcPath = new RegExp(config('paths').src.common); 266 | return srcThemes.test(filePath) || stagingThemes.test(filePath) 267 | || path.extname(filePath) === '.scss' || (path.extname(filePath) === '.css' && !srcPath.test(filePath)); 268 | } 269 | 270 | function _getCcaPath(filePath) { 271 | const configPaths = util.getConfiguredPaths(); 272 | const pathArray = filePath.split(path.sep); 273 | // cca index +1 to get one level down /jet-composites and +1 for the slice 274 | const ccaIndex = pathArray.indexOf(configPaths.components) + 2; 275 | return path.normalize(pathArray.slice(0, ccaIndex).join(path.sep)); 276 | } 277 | 278 | function _getThemeObjFromPath(filePath) { 279 | const allThemes = util.getAllThemes(); 280 | const result = {}; 281 | const themeName = allThemes.filter(singleTheme => filePath.indexOf(singleTheme) !== -1); 282 | if (themeName.length === 1) { 283 | result.name = themeName[0]; 284 | } else { 285 | // find the theme with longest legnth ['alta', 'alta_test'] 286 | result.name = themeName.reduce((acc, cur) => ((cur.length > acc.length) ? cur : acc), ''); 287 | } 288 | const allPlatforms = CONSTANTS.SUPPORTED_PLATFORMS; 289 | let themePlatform = config('platform'); 290 | allPlatforms.forEach((singlePlatform) => { 291 | themePlatform = (filePath.indexOf(singlePlatform) === -1) ? themePlatform : singlePlatform; 292 | }); 293 | result.platform = themePlatform; 294 | return result; 295 | } 296 | 297 | function _getThemeObjFromName(theme, opts) { 298 | if (theme === null) return null; 299 | if (theme.name === opts.theme.name) { 300 | return opts.theme; 301 | } 302 | return valid.getThemeObject(theme.name, theme.platform); 303 | } 304 | 305 | function _getBuildContext() { 306 | const validPlatform = config.get('platform'); 307 | const options = valid.buildOptions(config.get('serve'), validPlatform, true); 308 | const validBuildType = valid.buildType(options); 309 | options.buildType = validBuildType; 310 | options.cssonly = true; 311 | options.themes = undefined; // disable compiling all themes for livereload 312 | const context = 313 | { 314 | buildType: validBuildType, 315 | opts: options, 316 | platform: validPlatform 317 | }; 318 | return context; 319 | } 320 | -------------------------------------------------------------------------------- /lib/serveWeb.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * # Dependencies 11 | */ 12 | 13 | /* Oracle */ 14 | const config = require('./config'); 15 | const util = require('./util'); 16 | const indexHtmlInjector = require('./indexHtmlInjector'); 17 | const serveConnect = require('./serve/connect'); 18 | const serveWatch = require('./serve/watch'); 19 | const hookRunner = require('./hookRunner'); 20 | 21 | /** 22 | * # ServeWeb procedure 23 | * 24 | * @param {function} build - build action (build or not) 25 | * @public 26 | */ 27 | module.exports = (build) => { 28 | let beforeServeContext = {}; 29 | let connectOpts = {}; 30 | let serveOpts = {}; 31 | build() 32 | .then((context) => { 33 | connectOpts = _getConnectConfig(config.get('serve')); 34 | serveOpts = config.get('serve'); 35 | if (!context) { 36 | // eslint-disable-next-line no-param-reassign 37 | context = {}; 38 | } 39 | // eslint-disable-next-line no-param-reassign 40 | context.connectOpts = connectOpts; 41 | // eslint-disable-next-line no-param-reassign 42 | context.serveOpts = serveOpts; 43 | return hookRunner('before_serve', context); 44 | }) 45 | .then((returnedContext) => { 46 | beforeServeContext = returnedContext; 47 | _updateCspRuleForLivereload(); 48 | }) 49 | .then(() => { 50 | serveConnect(connectOpts, beforeServeContext); 51 | serveWatch(serveOpts.watch, serveOpts.livereloadPort, beforeServeContext); 52 | hookRunner('after_serve', beforeServeContext); 53 | }) 54 | .catch((error) => { 55 | util.log.error(error); 56 | }); 57 | }; 58 | 59 | function _getConnectConfig(opts) { 60 | const connectConfig = Object.assign({ 61 | livereloadPort: opts.livereloadPort, 62 | serverUrl: opts.serverUrl 63 | }, 64 | opts.connect.options 65 | ); 66 | if (connectConfig.buildType === 'dev') { 67 | connectConfig.keepalive = true; 68 | } 69 | return connectConfig; 70 | } 71 | 72 | /** 73 | * ## _updateCspRuleForLivereload 74 | * 75 | * If livereload is on, updates the CSP rule in index.html to allow connections 76 | * to the livereload server. 77 | * 78 | * @private 79 | * @returns {object} - resolved promise 80 | */ 81 | function _updateCspRuleForLivereload() { 82 | const serveConfigs = config.get('serve'); 83 | 84 | if (!serveConfigs.livereload) { 85 | return Promise.resolve(); 86 | } 87 | const opts = { stagingPath: config.get('paths').staging.web }; 88 | const context = { opts }; 89 | return indexHtmlInjector.injectLocalhostCspRule(context); 90 | } 91 | -------------------------------------------------------------------------------- /lib/serveWebFileChangeHandler.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | /** 10 | * # Dependencies 11 | */ 12 | 13 | /* 3rd party */ 14 | const fs = require('fs-extra'); 15 | 16 | /* Oracle */ 17 | const buildCommon = require('./buildCommon'); 18 | const util = require('./util'); 19 | const path = require('path'); 20 | 21 | /** 22 | * # serveWebFileChangeHandler 23 | * 24 | * @public 25 | * @param {object} options 26 | * @param {string} options.filePath 27 | * @param {object} options.buildContext 28 | * @returns {Promise} 29 | */ 30 | module.exports = function (filePath, buildContext) { 31 | return new Promise((resolve, reject) => { 32 | const pathComponents = util.getPathComponents(filePath); 33 | const configPaths = util.getConfiguredPaths(); 34 | const defaultDest = path.join( 35 | pathComponents.beg, 36 | configPaths.staging.web, 37 | pathComponents.end 38 | ); 39 | const isTypescriptFile = util.isTypescriptFile({ filePath }); 40 | const inTypescriptFolder = pathComponents.end.startsWith( 41 | path.join(path.sep, configPaths.src.typescript) 42 | ); 43 | let buildPromise = Promise.resolve(buildContext); 44 | /* Copies file over for the watch events */ 45 | if (_isIndexHtml(filePath)) { 46 | fs.copySync(filePath, defaultDest, { dereference: true }); 47 | buildPromise = buildCommon.injectTheme(buildContext) 48 | .then(buildCommon.injectLocalhostCspRule) 49 | .then(buildCommon.injectScripts); 50 | } else if (_isMainJs(filePath)) { 51 | fs.copySync(filePath, defaultDest, { dereference: true }); 52 | buildPromise = buildCommon.injectPaths(buildContext); 53 | } else if (util.isPathCCA(path.join(pathComponents.mid, pathComponents.end))) { 54 | const { pack, component } = util.getComponentInformationFromFilePath({ 55 | filePath: path.join(pathComponents.mid, pathComponents.end), 56 | filePathPointsToSrc: true 57 | }); 58 | const scriptsFolder = inTypescriptFolder ? 59 | configPaths.src.typescript : configPaths.src.javascript; 60 | // copy to versioned component staging location 61 | const componentStagingRoot = util.generatePathToComponentRoot({ 62 | context: buildContext, 63 | pack, 64 | component, 65 | root: configPaths.staging.web, 66 | scripts: scriptsFolder 67 | }); 68 | const relativePathToFile = path.relative( 69 | path.join(path.sep, scriptsFolder, configPaths.components, pack, component), 70 | pathComponents.end 71 | ); 72 | const filePathDest = path.join( 73 | pathComponents.beg, 74 | componentStagingRoot, 75 | relativePathToFile 76 | ); 77 | fs.copySync( 78 | filePath, 79 | filePathDest, 80 | { dereference: true } 81 | ); 82 | if (isTypescriptFile) { 83 | // is a typescript file so have to run the compiler 84 | // eslint-disable-next-line no-param-reassign 85 | buildContext.serving = true; 86 | // set file option with path to changed file that 87 | // needs to be compiled 88 | // eslint-disable-next-line no-param-reassign 89 | buildContext.opts.typescript = { 90 | ...(buildContext.opts.typescript || {}), 91 | file: path.join(componentStagingRoot, relativePathToFile) 92 | }; 93 | buildPromise = buildCommon.compileComponentTypescript({ 94 | context: buildContext, 95 | pack, 96 | component 97 | }); 98 | } else { 99 | // not a typescript file so no need to run compiler, simply 100 | // copy to the destination folder i.e web/js 101 | const componentStagingRootJs = util.generatePathToComponentRoot({ 102 | context: buildContext, 103 | pack, 104 | component, 105 | root: configPaths.staging.web, 106 | scripts: configPaths.src.javascript 107 | }); 108 | const relativePathToFileJs = path.relative( 109 | path.join(path.sep, scriptsFolder, configPaths.components, pack, component), 110 | pathComponents.end 111 | ); 112 | const filePathDestJs = path.join( 113 | pathComponents.beg, 114 | componentStagingRootJs, 115 | relativePathToFileJs 116 | ); 117 | fs.copySync( 118 | filePath, 119 | filePathDestJs, 120 | { dereference: true } 121 | ); 122 | } 123 | } else if (isTypescriptFile) { 124 | fs.copySync(filePath, defaultDest, { dereference: true }); 125 | // eslint-disable-next-line no-param-reassign 126 | buildContext.serving = true; 127 | // provide path to file so that we only compile the file that changed 128 | // eslint-disable-next-line no-param-reassign 129 | buildContext.opts.typescript = { 130 | ...(buildContext.opts.typescript || {}), 131 | file: path.join(configPaths.staging.web, pathComponents.end) 132 | }; 133 | buildPromise = buildCommon.compileApplicationTypescript(buildContext); 134 | } else if (inTypescriptFolder) { 135 | // copy to web/ts to prevent override during post-typescript copy 136 | fs.copySync(filePath, defaultDest, { dereference: true }); 137 | // copy to web/js since post-typescript copy not run 138 | const relativePathToFile = path.relative( 139 | path.join(path.sep, configPaths.src.typescript), 140 | pathComponents.end 141 | ); 142 | const filePathDest = path.join( 143 | pathComponents.beg, 144 | configPaths.staging.web, 145 | configPaths.src.javascript, 146 | relativePathToFile 147 | ); 148 | fs.copySync( 149 | filePath, 150 | filePathDest, 151 | { dereference: true } 152 | ); 153 | } else { 154 | fs.copySync(filePath, defaultDest, { dereference: true }); 155 | } 156 | buildPromise 157 | .then(resolve) 158 | .catch(reject); 159 | }); 160 | }; 161 | 162 | /** 163 | * ## _isIndexHtml 164 | * 165 | * @private 166 | * @param {string} filePath 167 | * @returns {boolean} 168 | */ 169 | function _isIndexHtml(filePath) { 170 | return path.basename(filePath) === 'index.html'; 171 | } 172 | 173 | /** 174 | * ## _isMainJs 175 | * 176 | * @private 177 | * @param {string} filePath 178 | * @returns {boolean} 179 | */ 180 | function _isMainJs(filePath) { 181 | return path.basename(filePath) === 'main.js'; 182 | } 183 | -------------------------------------------------------------------------------- /lib/strip.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 8 | 'use strict'; 9 | 10 | const glob = require('glob'); 11 | const fs = require('fs-extra'); 12 | const util = require('./util'); 13 | const path = require('path'); 14 | 15 | function _getGitIgnore() { 16 | const pathTogGitIgnore = util.destPath('.gitignore'); 17 | if (util.fsExistsSync(pathTogGitIgnore)) { 18 | return util.readFileSync(pathTogGitIgnore).split(/\r?\n/); 19 | } 20 | util.log.warning('No .gitignore file found. No files will be cleaned'); 21 | return []; 22 | } 23 | 24 | function _getStripList() { 25 | // Check for the presence of a 'stripList' property in oraclejetconfig.json. 26 | // If there, return its array 27 | return util.getOraclejetConfigJson().stripList; 28 | } 29 | 30 | function _getCleanFileList(list) { 31 | let fileList = []; 32 | list.forEach((file) => { 33 | if (file.length !== 0 && !/Thumbs\.db|\.DS_Store/.test(file)) { 34 | const exclusion = file.indexOf('!') === 0; 35 | let srcPattern = exclusion ? file.slice(1) : file; 36 | // .gitignore file pattern may start with '/', remove it for glob matching 37 | 38 | srcPattern = srcPattern[0] === '/' ? srcPattern.slice(1) : srcPattern; 39 | const match = glob.sync(util.posixPattern(util.destPath(srcPattern)), 40 | { cwd: util.destPath() }); 41 | fileList = exclusion ? util.difference(fileList, match) : util.union(fileList, match); 42 | } 43 | }); 44 | return fileList; 45 | } 46 | 47 | module.exports = function strip() { 48 | const stripList = _getStripList(); 49 | const fileList = _getCleanFileList(stripList || _getGitIgnore()); 50 | return new Promise((resolve, reject) => { 51 | fileList.forEach((file) => { 52 | if (path.extname(file) === '') { 53 | fs.removeSync(file); 54 | } else { 55 | fs.remove(file, (err) => { 56 | if (err) reject(err); 57 | }); 58 | } 59 | }); 60 | 61 | resolve(); 62 | }); 63 | }; 64 | -------------------------------------------------------------------------------- /lib/svg.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | 'use strict'; 8 | 9 | const fs = require('fs-extra'); 10 | const { optimize } = require('svgo'); 11 | const util = require('./util'); 12 | const path = require('path'); 13 | const glob = require('glob'); 14 | const config = require('./config'); 15 | const CONSTANTS = require('./constants'); 16 | 17 | /** 18 | * Return the promise to optimize user provided svg files, remove redundant content 19 | * @param {Object} Running context that contains all options 20 | * @returns {Promise} return the promise 21 | */ 22 | 23 | function _svgMin(context) { 24 | const opts = context.opts.svgMin; 25 | const fileResult = util.getFileList(context.buildType, opts.fileList); 26 | return new Promise((resolve, reject) => { 27 | try { 28 | fileResult.forEach((file) => { 29 | const src = util.readFileSync(file.src); 30 | const result = optimize(src, opts.options); 31 | if (result.error) { 32 | // The returned result object--which can be of type OptimizedError or 33 | // OptimizedSvg--includes attributes error (with the error message) and 34 | // modernError (giving more info about the error--plus the message--and 35 | // other details like where the error is originated). Therefore, passing 36 | // result.modernError is more ideal in this case after adding the file 37 | // causing the error in its message 38 | // Source: https://unpkg.com/browse/@types/svgo@2.6.3/index.d.ts#L724. 39 | if (result.modernError) { 40 | result.modernError.message = `${result.modernError.message} Error caused by file: ${file.src}`; 41 | reject(result.modernError); 42 | } else { 43 | result.error = `${result.error} Error caused by file: ${file.src}`; 44 | reject(result.error); 45 | } 46 | } 47 | fs.outputFileSync(path.resolve(file.dest), result.data); 48 | }); 49 | resolve(context); 50 | } catch (error) { 51 | reject(error); 52 | } 53 | }); 54 | } 55 | 56 | // Determine if there are any user svg files in given theme 57 | function _hasUserSvg(theme) { 58 | const themePath = `${config('paths').src.common}/${config('paths').src.themes}/${theme}`; 59 | const fileList = glob.sync('*/images/*.svg', { cwd: themePath }); 60 | return fileList.length > 0; 61 | } 62 | 63 | /** 64 | * Return the promise to combine user provided svg files into a sprite.svg 65 | * and update the _oj.common.sprite.scss 66 | * @param {Object} Running context that contains all options 67 | * @returns {Promise} return the promise 68 | */ 69 | function _svgSprite(context) { 70 | const defaultPadding = 2; 71 | const opts = context.opts.svgSprite; 72 | const padding = opts.options.shape.spacing.padding || defaultPadding; 73 | const defaultTheme = (!util.getInstalledCssPackage()) ? 74 | CONSTANTS.DEFAULT_THEME : CONSTANTS.DEFAULT_PCSS_THEME; 75 | const themePath = path.join(`${config('paths').staging.themes}`, defaultTheme); 76 | const needsSprite = (padding !== defaultPadding) || _hasUserSvg(defaultTheme); 77 | if (!needsSprite) { 78 | // Exit 79 | return Promise.resolve(context); 80 | } 81 | 82 | return new Promise((resolve, reject) => { 83 | try { 84 | util.readDirSync(themePath).forEach((dir) => { 85 | if (CONSTANTS.SUPPORTED_THEME_PLATFORMS.indexOf(dir) !== -1) { 86 | if (!util.getInstalledCssPackage()) { 87 | opts.options.mode = _svgSpriteMode(util.mapToSourceSkinName(dir)); 88 | } else { 89 | opts.options.mode = _svgPcssSpriteMode(util.mapToPcssSourceSkinName(dir)); 90 | } 91 | const fileList = glob.sync('images/*.svg', { cwd: path.join(themePath, dir) }); 92 | if (opts.options.shape.spacing.padding) opts.options.shape.spacing.padding = padding; 93 | let SVGSpriter; 94 | try { 95 | SVGSpriter = require('svg-sprite'); // eslint-disable-line 96 | } catch (e) { 97 | // No svg-sprite module installed 98 | util.log.error('svg-sprite module likely not installed but needed for custom .svg files\n\nInstall using "npm install svg-sprite" and rebuild'); 99 | } 100 | const spriter = new SVGSpriter(opts.options); 101 | fileList.forEach((file) => { 102 | if (path.basename(file) !== 'sprite.svg') { 103 | const fileBase = path.join(themePath, dir); 104 | spriter.add(path.resolve(file), path.basename(file), 105 | util.readFileSync(path.resolve(fileBase, file))); 106 | } 107 | }); 108 | spriter.compile((error, result) => { 109 | Object.keys(result).forEach((mode) => { 110 | Object.keys(result[mode]).forEach((resource) => { 111 | const output = result[mode][resource]; 112 | const svgDestSuffix = 'images/sprites/sprite.svg'; 113 | let dest; 114 | let filePrefix; 115 | if (!util.getInstalledCssPackage()) { 116 | dest = path.join(config('paths').staging.themes, CONSTANTS.DEFAULT_THEME, dir, svgDestSuffix); 117 | } else { 118 | dest = path.join(config('paths').staging.themes, CONSTANTS.DEFAULT_PCSS_THEME, dir, svgDestSuffix); 119 | } 120 | dest = path.extname(output.path) === '.svg' ? dest : output.path; 121 | if (!util.getInstalledCssPackage()) { 122 | filePrefix = util.mapToSourceSkinName(dir).replace('-', '.'); 123 | } else { 124 | filePrefix = util.mapToPcssSourceSkinName(dir).replace('-', '.'); 125 | } 126 | fs.outputFileSync(dest, output.contents); 127 | const content = util.readFileSync(dest).replace(/_(?!.+\.svg)/g, '-'); 128 | if (filePrefix !== 'common') { 129 | fs.outputFileSync(dest, content.replace(/common\.sprite/g, `${filePrefix}.sprite`)); 130 | } else { 131 | fs.outputFileSync(dest, content.replace(/oj-image-url/g, 'oj-common-image-url')); 132 | } 133 | }); 134 | }); 135 | }); 136 | } 137 | }); 138 | resolve(context); 139 | } catch (error) { 140 | reject(error); 141 | } 142 | }); 143 | } 144 | 145 | /** 146 | * Return the mode configuration for svg spriter 147 | * @param {String} Skin name 148 | * @returns {Object} return the object configuration 149 | */ 150 | function _svgSpriteMode(skin) { 151 | const filePrefix = skin.replace('-', '.'); 152 | return { 153 | css: { 154 | render: { 155 | scss: { 156 | template: `${CONSTANTS.PATH_TO_ORACLEJET}/scss/templates/svg-sprite-template.scss.txt`, 157 | dest: `${CONSTANTS.PATH_TO_ORACLEJET}/scss/${skin}/widgets/_oj.${filePrefix}.sprite.scss` 158 | } 159 | }, 160 | layout: 'horizontal', 161 | dest: '.', 162 | sprite: 'sprite.svg', 163 | bust: false, 164 | example: false 165 | } 166 | }; 167 | } 168 | 169 | /** 170 | * Return the mode configuration for pcss svg spriter 171 | * @param {String} Skin name 172 | * @returns {Object} return the object configuration 173 | */ 174 | function _svgPcssSpriteMode() { 175 | return { 176 | css: { 177 | render: { 178 | scss: { 179 | template: `${CONSTANTS.PATH_TO_ORACLEJET}/scss/templates/svg-sprite-template.scss.txt`, 180 | dest: `${CONSTANTS.PATH_TO_ORACLEJET}/pcss/oj/${util.getJETVersionV(util.getJETVersion())}/oj/icons/themes/redwood/common/_oj-sprite.scss` 181 | } 182 | }, 183 | layout: 'horizontal', 184 | dest: '.', 185 | sprite: 'sprite.svg', 186 | bust: false, 187 | example: false 188 | } 189 | }; 190 | } 191 | 192 | module.exports = { 193 | spriteSvg: function _spriteSvg(context) { 194 | return new Promise((resolve, reject) => { 195 | try { 196 | _svgMin(context) 197 | .then(_svgSprite) 198 | .then(() => { 199 | resolve(context); 200 | }); 201 | } catch (error) { 202 | reject(error); 203 | } 204 | }); 205 | } 206 | }; 207 | -------------------------------------------------------------------------------- /lib/templates/pack/component.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@pack@", 3 | "version": "1.0.0", 4 | "jetVersion": "18.1.0", 5 | "type": "pack", 6 | "displayName": "A user friendly, translatable name of the pack.", 7 | "description": "A translatable high-level description for the pack.", 8 | "dependencies": {} 9 | } 10 | -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/icons/apple-icon-180.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/icons/apple-icon-180.png -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/icons/icon-192x192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/icons/icon-192x192.png -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/icons/icon-512x512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/icons/icon-512x512.png -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-1125x2436.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-1125x2436.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-1242x2208.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-1242x2208.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-1242x2688.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-1242x2688.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-1536x2048.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-1536x2048.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-1668x2224.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-1668x2224.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-1668x2388.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-1668x2388.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-2048x2732.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-2048x2732.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-640x1136.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-640x1136.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-750x1334.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-750x1334.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/assets/splashscreens/splash-828x1792.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/oracle/oraclejet-tooling/8a9f45c7bbe74f8979c8ad06b248ec57b6b8d2ea/lib/templates/serviceWorkers/assets/splashscreens/splash-828x1792.jpg -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@AppName@", 3 | "short_name": "PWA", 4 | "description": "An Oracle JavaScript Extension Toolkit(JET) web app.", 5 | "start_url": "index.html", 6 | "scope": "/", 7 | "display": "standalone", 8 | "background_color": "#FFFFFF", 9 | "theme_color": "#000000", 10 | "orientation": "portrait", 11 | "icons": [ 12 | { 13 | "src": "assets/icons/icon-192x192.png", 14 | "sizes": "192x192", 15 | "type": "image/png" 16 | }, 17 | { 18 | "src": "assets/icons/icon-512x512.png", 19 | "sizes": "512x512", 20 | "type": "image/png" 21 | } 22 | ], 23 | "categories": ["utilities", "productivity"], 24 | "lang": "en-US", 25 | "dir": "ltr" 26 | } -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/sw.txt: -------------------------------------------------------------------------------- 1 | const CACHE_NAME = '@AppName@'; 2 | const resourcesToCache = ['index.html', 'manifest.json', 'js/', 'css/', 'assets/']; 3 | 4 | // install service worker and cache resources defined in resourcesToCache 5 | self.addEventListener('install', (event) => { 6 | event.waitUntil( 7 | caches.open(CACHE_NAME).then(cache => ( 8 | cache.addAll(resourcesToCache) 9 | )) 10 | ); 11 | }); 12 | 13 | // return response from cache if there's a match 14 | // otherwise fetch from server and add it to cache 15 | self.addEventListener('fetch', (event) => { 16 | event.respondWith( 17 | caches.match(event.request).then((response) => { 18 | return response || fetch(event.request).then((response) => { 19 | return caches.open(CACHE_NAME).then((cache) => { 20 | cache.put(event.request, response.clone()); 21 | return response; 22 | }); 23 | }); 24 | }) 25 | ); 26 | }); 27 | -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/swInit.txt: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | -------------------------------------------------------------------------------- /lib/templates/serviceWorkers/swinit._js: -------------------------------------------------------------------------------- 1 | if ('serviceWorker' in navigator) { 2 | navigator.serviceWorker.register('sw.js').then((registration) => { 3 | // Registration was successful 4 | console.log('@AppName@ ServiceWorker registration successful with scope: ', registration.scope); 5 | }).catch((err) => { 6 | // registration failed 7 | console.log('@AppName@ ServiceWorker registration failed: ', err); 8 | }); 9 | } 10 | -------------------------------------------------------------------------------- /lib/templates/typescript/tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compileOnSave": true, 3 | "compilerOptions": { 4 | "baseUrl": ".", 5 | "target": "es6", 6 | "strict": true, 7 | "module": "amd", 8 | "moduleResolution": "node", 9 | "jsx": "react-jsx", 10 | "jsxImportSource": "preact", 11 | "lib": ["es2021", "dom", "esnext.asynciterable"], 12 | "typeRoots": ["./node_modules/@oracle/oraclejet/dist/types", "./node_modules/@types"], 13 | "paths": { 14 | "ojs/*": ["./node_modules/@oracle/oraclejet/dist/types/*"], 15 | "@oracle/oraclejet-preact/*": ["./node_modules/@oracle/oraclejet-preact/*"], 16 | "oj-c/*": ["./node_modules/@oracle/oraclejet-core-pack/oj-c/types/*"], 17 | "preact": ["./node_modules/preact"] 18 | }, 19 | "declaration": true, 20 | "noEmitOnError": true, 21 | "experimentalDecorators": true, 22 | "skipLibCheck": true, 23 | "removeComments": true 24 | }, 25 | "include": ["./src/ts/**/*"] 26 | } -------------------------------------------------------------------------------- /lib/templates/webpack/ojet.config.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | module.exports = { 8 | /** 9 | * 10 | * @param {object} options.context - ojet build context which contains useful fields like 11 | * buildType 12 | * @param {object} options.config - Default webpack config generated by ojet. You can 13 | * add to it, remove from it or update it using webpack-merge which was 14 | * installed alongside webpack. If desired, you can create your own config 15 | * and return it which will override the default config. Consider the examples below: 16 | * 17 | * 1. Adding a new plugin or optimization configuration into the default config: 18 | * 19 | * config.plugins.push(new SomeWebpackPlugin()); 20 | * 21 | * config.optimization = {...config.optimization, splitChunks: { chunks: 'async' }}; 22 | * 23 | * config.output.publicPath = 'https://yourcdnlink.com/'; 24 | * 25 | * 2. Alternatively, you can implement a custom config to override the default: 26 | * 27 | * config = {}; 28 | * 29 | * config.plugins = [new SomeWebpackPlugin()]; 30 | * 31 | * config.optimization = {minimize: true, splitChunks: { chunks: 'all' }}; 32 | * 33 | * config.module.rules.push({test: /\.css$/, use: ['style-loader', 'css-loader']}); 34 | * 35 | * config.devServer = {static: {directory: path.join(_dirname, 'web')}, hot: true, port: 9000}; 36 | * 37 | * @returns {object|undefined} 38 | */ 39 | webpack: ({ context, config }) => { 40 | if (context.buildType === 'release') { 41 | // update config with release / production options 42 | } else { 43 | // update config with development options 44 | } 45 | // only have to return if new config object was created but 46 | // since it doesn't matter always returning the config is good 47 | // practice 48 | return { context, webpack: config }; 49 | } 50 | }; 51 | -------------------------------------------------------------------------------- /lib/webpack/build.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | module.exports = (options) => { 8 | // eslint-disable-next-line global-require 9 | const ojetUtils = require('../util'); 10 | // eslint-disable-next-line global-require 11 | const setup = require('./setup'); 12 | // eslint-disable-next-line global-require 13 | const hookRunner = require('../hookRunner'); 14 | // eslint-disable-next-line global-require 15 | const webpackUtils = require('./utils'); 16 | return new Promise((resolve, reject) => { 17 | ojetUtils.log('Building with Webpack'); 18 | const buildContext = webpackUtils.createContext({ options, platform: 'web' }); 19 | const { context, webpack, webpackConfig } = setup(buildContext); 20 | hookRunner('before_build', context); 21 | webpack(webpackConfig, (err, stats) => { 22 | if (err) { 23 | console.error(err.stack || err); 24 | if (err.details) { 25 | console.error(err.details); 26 | } 27 | reject(err.details); 28 | } 29 | if (stats.compilation.errors && stats.compilation.errors.length > 0) { 30 | console.error(stats.compilation.errors); 31 | reject(stats.compilation.errors); 32 | } 33 | console.log(stats.toString()); 34 | hookRunner('after_build', context); 35 | resolve(context); 36 | }); 37 | }).then(() => { 38 | if (ojetUtils.isTypescriptApplication()) { 39 | webpackUtils.organizeTypeDefinitions(); 40 | } 41 | }); 42 | }; 43 | -------------------------------------------------------------------------------- /lib/webpack/custom-tsc/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const path = require('path'); 8 | const ojetUtils = require('../../util'); 9 | const webpackUtils = require('../utils'); 10 | 11 | // eslint-disable-next-line import/no-dynamic-require 12 | const decoratorTransformer = require(path.join(webpackUtils.oracleJetDistPath, 'custom-tsc/decoratorTransformer')); 13 | // eslint-disable-next-line import/no-dynamic-require 14 | const metadataTransformer = require(path.join(webpackUtils.oracleJetDistPath, 'custom-tsc/metadataTransformer')); 15 | const constants = require('../../constants'); 16 | 17 | const configPaths = ojetUtils.getConfiguredPaths(); 18 | 19 | const buildOptions = { 20 | version: '', 21 | jetVersion: '', 22 | debug: false, 23 | dtDir: path.join(configPaths.staging.web, configPaths.src.typescript, constants.COMPONENTS_DT), 24 | templatePath: path.join(ojetUtils.getOraclejetPath(), constants.PATH_TO_CUSTOM_TSC_TEMPLATES), 25 | tsBuiltDr: path.join(configPaths.staging.web, configPaths.src.javascript, configPaths.components), 26 | ...(ojetUtils.needsMainEntryFile() && { mainEntryFile: 'loader.d.ts' }), 27 | typesDir: path.join(configPaths.staging.web, configPaths.src.javascript, configPaths.components) 28 | }; 29 | 30 | function createTransformer(baseTransformer) { 31 | return program => (baseTransformer.default(program, buildOptions)); 32 | } 33 | 34 | module.exports = { 35 | metadataTransformer: createTransformer(metadataTransformer), 36 | decoratorTransformer: createTransformer(decoratorTransformer) 37 | }; 38 | -------------------------------------------------------------------------------- /lib/webpack/serve.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | module.exports = (options) => { 8 | // eslint-disable-next-line global-require 9 | const webpackUtils = require('./utils'); 10 | // eslint-disable-next-line global-require 11 | const setup = require('./setup'); 12 | // eslint-disable-next-line global-require 13 | const ojetUtils = require('../util'); 14 | // eslint-disable-next-line global-require 15 | const hookRunner = require('../hookRunner'); 16 | // eslint-disable-next-line import/no-dynamic-require, global-require 17 | const WebpackDevServer = ojetUtils.requireLocalFirst('webpack-dev-server'); 18 | return new Promise(async () => { 19 | ojetUtils.log('Serving with Webpack'); 20 | const serveContext = webpackUtils.createContext({ options, platform: 'web' }); 21 | const { webpack, context, webpackConfig } = setup(serveContext); 22 | const compiler = webpack(webpackConfig); 23 | if (webpackConfig.devServer) { 24 | const devServerOptions = { ...webpackConfig.devServer }; 25 | // Update the context object: 26 | context.compiler = compiler; 27 | context.port = devServerOptions.port || 8001; 28 | context.host = devServerOptions.host || '127.0.0.1'; 29 | // Run the before_serve hook with the updated context: 30 | await hookRunner('before_serve', context); 31 | const server = new WebpackDevServer(devServerOptions, context.compiler); 32 | // Start the server: 33 | await server.start(context.port, context.host); 34 | // Run the after serve hook: 35 | await hookRunner('after_serve', context); 36 | } else { 37 | ojetUtils.log.error( 38 | ` 39 | Serving with the --release flag is currently not supported. Please 40 | run 'ojet build --release' and then serve the built folder 41 | using your favorite command line server (e.g. http-server). 42 | `.replace(/\n/g, '').replace(/\s+/g, ' ') 43 | ); 44 | } 45 | }); 46 | }; 47 | -------------------------------------------------------------------------------- /lib/webpack/setup.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const path = require('path'); 8 | const ojetUtils = require('../util'); 9 | const webpackUtils = require('./utils'); 10 | const config = require('../config'); 11 | const generateComponentsCache = require('../buildCommon/generateComponentsCache'); 12 | const constants = require('../constants'); 13 | 14 | const HtmlReplaceWebpackPlugin = ojetUtils.requireLocalFirst('html-replace-webpack-plugin'); 15 | 16 | module.exports = (context) => { 17 | config.set('componentsCache', generateComponentsCache({ 18 | context 19 | })); 20 | config.set('_context', context); 21 | let webpackConfig; 22 | if (context.buildType === 'release') { 23 | // eslint-disable-next-line global-require 24 | webpackConfig = require('./webpack.production'); 25 | } else { 26 | // eslint-disable-next-line global-require 27 | webpackConfig = require('./webpack.development'); 28 | } 29 | 30 | // Process theme files and copy them to staging: 31 | webpackUtils.copyRequiredAltaFilesToStaging(context); 32 | 33 | const pathToOjetConfig = path.resolve(constants.PATH_TO_OJET_CONFIG); 34 | 35 | // eslint-disable-next-line global-require, import/no-dynamic-require 36 | const ojetConfig = require(pathToOjetConfig); 37 | 38 | // This will have the new context and webpack config objects if any 39 | // changes will be made in the ojet.config.js file 40 | let newConfig; 41 | 42 | if (ojetConfig.webpack) { 43 | newConfig = ojetConfig.webpack({ 44 | context, 45 | config: webpackConfig 46 | }) || webpackConfig; 47 | } 48 | 49 | // In cases where publicPath is declared, then the the html webpack plugin 50 | // will automatically inject the compiled styles; hence, there is no need 51 | // for style link tags: 52 | const outputConfig = newConfig.webpack.output; 53 | if (outputConfig && outputConfig.publicPath) { 54 | newConfig.webpack.plugins.push( 55 | new HtmlReplaceWebpackPlugin( 56 | [ 57 | { 58 | pattern: '', 59 | replacement: '' 60 | }, 61 | { 62 | pattern: '', 63 | replacement: '' 64 | } 65 | ] 66 | ) 67 | ); 68 | } else { 69 | newConfig.webpack.plugins.push( 70 | new HtmlReplaceWebpackPlugin( 71 | [ 72 | { 73 | pattern: '', 74 | replacement: '' 75 | }, 76 | { 77 | pattern: '', 78 | replacement: webpackUtils.getStyleLinkTags() 79 | } 80 | ] 81 | ) 82 | ); 83 | } 84 | 85 | return { 86 | webpack: ojetUtils.requireLocalFirst('webpack'), 87 | webpackConfig: newConfig.webpack, 88 | context: newConfig.context 89 | }; 90 | }; 91 | -------------------------------------------------------------------------------- /lib/webpack/utils.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const path = require('path'); 8 | const glob = require('glob'); 9 | const fs = require('fs-extra'); 10 | const ojetUtils = require('../util'); 11 | const injector = require('../indexHtmlInjector'); 12 | const config = require('../config'); 13 | const valid = require('../validations'); 14 | 15 | const configPaths = ojetUtils.getConfiguredPaths(); 16 | const exchangeComponentsPath = path.resolve(configPaths.exchangeComponents); 17 | const localComponentsPath = path.resolve( 18 | configPaths.src.common, 19 | configPaths.src.typescript, 20 | configPaths.components 21 | ); 22 | const oracleJetPreactPath = 'node_modules/@oracle/oraclejet-preact/es'; 23 | const oracleJetDistPath = path.join(ojetUtils.getOraclejetPath(), 'dist'); 24 | const oracleJetDistCssPath = path.join(oracleJetDistPath, 'css'); 25 | const oracleJetDistJsLibsPath = path.join(oracleJetDistPath, 'js/libs'); 26 | // eslint-disable-next-line no-useless-escape 27 | const htmlTokenPattern = /()?/g; 28 | // eslint-disable-next-line no-useless-escape 29 | const htmlEndInjectorTokenPattern = /()/g; 30 | // eslint-disable-next-line no-useless-escape 31 | const htmlAppCssLinkTokenPattern = /<\w+\s*\w+[^abc]+css[^abc]app.css[^abc]+css[^abc]+>/g; 32 | 33 | /** 34 | * ## isWebComponent 35 | * 36 | */ 37 | function isWebComponent(resourcePath) { 38 | let component; 39 | const normalizedResourcePath = path.normalize(resourcePath); 40 | if (normalizedResourcePath.startsWith(exchangeComponentsPath)) { 41 | component = path.relative(exchangeComponentsPath, normalizedResourcePath).split(path.sep)[0]; 42 | } else if (normalizedResourcePath.startsWith(localComponentsPath)) { 43 | component = path.relative(localComponentsPath, normalizedResourcePath).split(path.sep)[0]; 44 | } 45 | return component === undefined ? false : !!ojetUtils.getComponentsCache()[component]; 46 | } 47 | 48 | /** 49 | * ## organizeTypeDefinitions 50 | * 51 | */ 52 | function organizeTypeDefinitions() { 53 | const tsFilesTypesFolder = path.resolve(configPaths.staging.stagingPath, 'types'); 54 | ojetUtils.ensureDir(tsFilesTypesFolder); 55 | if (fs.existsSync(tsFilesTypesFolder)) { 56 | // get all *.d.ts files not in types or min (release build) 57 | glob.sync( 58 | ojetUtils.posixPattern(path.join(configPaths.src.common, '**/*.d.ts')), { 59 | ignore: ['**/types/**', '**/min/**'] 60 | } 61 | ).forEach((filePath) => { 62 | // loop through found *.d.ts files 63 | if (ojetUtils.fsExistsSync(path.join( 64 | tsFilesTypesFolder, 65 | path.relative(configPaths.src.common, filePath) 66 | ))) { 67 | // already exists in types folder, delete 68 | fs.removeSync(filePath); 69 | } else if (path.basename(filePath).startsWith('exports_')) { 70 | // special build time resource generated by custom-tsc, delete 71 | fs.removeSync(filePath); 72 | } else { 73 | // not in types folder, move into 74 | fs.moveSync(filePath, path.join( 75 | tsFilesTypesFolder, 76 | path.relative(configPaths.src.common, filePath) 77 | )); 78 | } 79 | }); 80 | } 81 | } 82 | 83 | /** 84 | * ## getEntryFilePath 85 | * 86 | * @returns {string} path to the root file 87 | */ 88 | function getEntryFilePath() { 89 | if (ojetUtils.isVDOMApplication()) { 90 | return path.resolve(configPaths.src.common, 'index.ts'); 91 | } 92 | if (ojetUtils.isTypescriptApplication()) { 93 | return path.resolve(configPaths.src.common, 'ts', 'root.ts'); 94 | } 95 | return path.resolve(configPaths.src.common, 'js', 'root.js'); 96 | } 97 | 98 | /** 99 | * ## getRootPath 100 | * 101 | * @returns {string} path to root folder 102 | */ 103 | function getRootPath() { 104 | if (ojetUtils.isVDOMApplication()) { 105 | return path.resolve(configPaths.staging.web, 106 | configPaths.src.javascript); 107 | } 108 | if (ojetUtils.isTypescriptApplication()) { 109 | return path.resolve(configPaths.src.common, 110 | configPaths.src.typescript); 111 | } 112 | return path.resolve(configPaths.src.common, 113 | configPaths.src.javascript); 114 | } 115 | 116 | /** 117 | * ## getThemeStyleArray 118 | * 119 | * @param {Object} context 120 | * @returns {Array} array with paths to theme files 121 | */ 122 | function getThemeStyleArray(context) { 123 | let themeStyleArray = []; 124 | const css = config('paths').src.styles; 125 | const theme = context.opts.theme; 126 | const buildType = context.buildType; 127 | const baseTheme = theme.basetheme || theme.name; 128 | const linkBase = injector.getStyleLinkBase(css, theme, buildType); 129 | if (linkBase.default && (baseTheme === 'redwood' || baseTheme === 'stable')) { 130 | themeStyleArray = [linkBase.default, linkBase.preact]; 131 | } else if (baseTheme === 'redwood' || baseTheme === 'stable') { 132 | themeStyleArray = [linkBase.created, linkBase.preact]; 133 | } 134 | return themeStyleArray; 135 | } 136 | 137 | /** 138 | * ## getCustomThemeEntryObj 139 | * 140 | * @returns {Object} 141 | */ 142 | function getCustomThemeEntryObj() { 143 | const customThemeEntryObj = {}; 144 | const context = config.get('_context'); 145 | const css = config('paths').src.styles; 146 | const theme = context.opts.theme; 147 | const buildType = context.buildType; 148 | const linkBase = injector.getStyleLinkBase(css, theme, buildType); 149 | // Path to custom theme in web. Using this path as the key of the 150 | // entry object, then webpack will generate this path in web and 151 | // emit the processed css files into it: 152 | const pathToCustomThemeInWeb = path.join( 153 | theme.name, 154 | theme.version, 155 | theme.platform, 156 | theme.name 157 | ); 158 | // Path to custom theme in src. This will be our entry file for the 159 | // custom theme: 160 | const pathToCustomThemeInSrc = path.resolve( 161 | config('paths').src.common, 162 | 'themes', 163 | theme.name, 164 | theme.platform, 165 | `${theme.name}.scss` 166 | ); 167 | 168 | if (linkBase.created && !(theme.name === 'redwood' || theme.name === 'stable')) { 169 | customThemeEntryObj[`${pathToCustomThemeInWeb}`] = [pathToCustomThemeInSrc]; 170 | } 171 | 172 | return customThemeEntryObj; 173 | } 174 | 175 | /** 176 | * ## getEntryObject 177 | * 178 | * @returns {Object} 179 | */ 180 | function getEntryObject() { 181 | const customThemeEntryObj = getCustomThemeEntryObj(); 182 | const applicationEntryFile = getEntryFilePath(); 183 | return { 184 | ...customThemeEntryObj, 185 | main: applicationEntryFile 186 | }; 187 | } 188 | 189 | /** 190 | * ## getStyleLinkTags 191 | * 192 | * @returns {string} concatenated style link tag strings 193 | */ 194 | function getStyleLinkTags() { 195 | let themeStyleLinkTags = ''; 196 | const context = config.get('_context'); 197 | 198 | // Generate style link tags for theme styles: 199 | const pathsToThemeStyles = getThemeStyleArray(context); 200 | const linkTag = ''; 201 | pathsToThemeStyles.forEach((pathToThemeStyle) => { 202 | themeStyleLinkTags = themeStyleLinkTags.concat(linkTag.replace('%s', `/${pathToThemeStyle}`).concat('\n')); 203 | }); 204 | 205 | // Generate link tags for the general app style: 206 | let pathToAppStyle = ''; 207 | if (context.buildType === 'release' && !ojetUtils.isVDOMApplication()) { 208 | pathToAppStyle = path.join(`/${configPaths.src.styles}`, 'app-min.css'); 209 | } else { 210 | pathToAppStyle = path.join(`/${configPaths.src.styles}`, 'app.css'); 211 | } 212 | const appStyleLinkTag = ``; 213 | 214 | // Finally, concatenate themeStyleLinkTags with appStyleLinkTag: 215 | const styleLinkTags = `${themeStyleLinkTags}\n${appStyleLinkTag}`; 216 | 217 | return styleLinkTags; 218 | } 219 | 220 | /** 221 | * ## copyRequiredAltaFilesToStaging 222 | * 223 | * @param {Object} context 224 | */ 225 | function copyRequiredAltaFilesToStaging(context) { 226 | // The required folders will be 'common' and the chosen platform (web, windows, etc). 227 | // We need the contents of these folders because we are redirecting some paths in the 228 | // generated css file (see webpack.common under the css-fix-url loader) to the resources 229 | // that are only found in the required folders. 230 | const requiredFolders = ['common', context.platform]; 231 | requiredFolders.forEach((folder) => { 232 | const scrPath = path.resolve(configPaths.staging.themes, 'alta', folder); 233 | let destPath = path.resolve(configPaths.staging.web, configPaths.src.styles, 'alta'); 234 | destPath = !ojetUtils.useUnversionedStructure(context) ? path.join(destPath, `${ojetUtils.getJETVersion()}`, folder) : path.join(destPath, folder); 235 | if (fs.existsSync(scrPath)) { 236 | fs.copySync(scrPath, destPath, { 237 | dereference: true 238 | }); 239 | } 240 | }); 241 | } 242 | 243 | /** 244 | * ## createContext 245 | * 246 | * @param {Object} options 247 | * @param {String} platform 248 | */ 249 | function createContext({ 250 | options, 251 | platform 252 | }) { 253 | config.loadOraclejetConfig(platform); 254 | const validPlatform = valid.platform(platform); 255 | const validOptions = valid.buildOptions(options, validPlatform); 256 | const validBuildType = valid.buildType(validOptions); 257 | return { 258 | buildType: validBuildType, 259 | opts: validOptions, 260 | platform: validPlatform 261 | }; 262 | } 263 | 264 | /** 265 | * ## getCopyPluginPatterns 266 | * 267 | * @param {Array} platform 268 | */ 269 | function getCopyPluginPatterns() { 270 | const context = config.get('_context'); 271 | const theme = context.opts.theme; 272 | const baseTheme = theme.basetheme || theme.name; 273 | 274 | if (baseTheme === 'redwood' || baseTheme === 'stable') { 275 | return [{ 276 | from: path.join(configPaths.src.common, configPaths.src.styles), 277 | to: path.join(configPaths.src.styles), 278 | }, 279 | { 280 | from: path.join(oracleJetDistCssPath, baseTheme), 281 | // Treverse the folder and emit the oj- prefix in css files 282 | // starting with such or replace the '-min' with '.min' in minified 283 | // styles. We are doing so to abide by the naming convention. 284 | to: ({ absoluteFilename }) => { 285 | const stat = fs.lstatSync(absoluteFilename); 286 | let filename = path.basename(absoluteFilename); 287 | const pathToPlatformFolder = path.join( 288 | configPaths.src.styles, 289 | baseTheme, 290 | ojetUtils.getJETVersion(), 291 | theme.platform 292 | ); 293 | if (stat.isFile() && filename.includes(`oj-${baseTheme}`)) { 294 | filename = filename.replace(`oj-${baseTheme}`, baseTheme); 295 | if (filename.includes(`${baseTheme}-min`)) { 296 | filename = filename.replace(`${baseTheme}-min`, `${baseTheme}.min`); 297 | } 298 | return path.join(pathToPlatformFolder, filename); 299 | } 300 | return pathToPlatformFolder; 301 | }, 302 | noErrorOnMissing: true 303 | }, 304 | { 305 | from: path.join(oracleJetPreactPath, `Theme-${baseTheme}`), 306 | to: path.join(configPaths.src.styles, `theme-${baseTheme}`, ojetUtils.getJETVersion(), theme.platform), 307 | noErrorOnMissing: true 308 | } 309 | ]; 310 | } 311 | 312 | return [{ 313 | from: path.join(configPaths.src.common, configPaths.src.styles), 314 | to: path.join(configPaths.src.styles), 315 | }]; 316 | } 317 | 318 | module.exports = { 319 | isWebComponent, 320 | localComponentsPath, 321 | exchangeComponentsPath, 322 | oracleJetDistPath, 323 | oracleJetDistCssPath, 324 | oracleJetDistJsLibsPath, 325 | htmlTokenPattern, 326 | htmlEndInjectorTokenPattern, 327 | htmlAppCssLinkTokenPattern, 328 | getEntryFilePath, 329 | organizeTypeDefinitions, 330 | getRootPath, 331 | getThemeStyleArray, 332 | getStyleLinkTags, 333 | copyRequiredAltaFilesToStaging, 334 | createContext, 335 | getCopyPluginPatterns, 336 | getCustomThemeEntryObj, 337 | getEntryObject 338 | }; 339 | -------------------------------------------------------------------------------- /lib/webpack/webpack.common.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const path = require('path'); 8 | const ojetUtils = require('../util'); 9 | const webpackUtils = require('./utils'); 10 | // eslint-disable-next-line import/no-dynamic-require 11 | const WebpackRequireFixupPlugin = require(path.join(webpackUtils.oracleJetDistPath, 'webpack-tools/plugins/WebpackRequireFixupPlugin')); 12 | 13 | const WebpackCopyPlugin = ojetUtils.requireLocalFirst('copy-webpack-plugin'); 14 | const HtmlWebpackPlugin = ojetUtils.requireLocalFirst('html-webpack-plugin'); 15 | const MiniCssExtractPlugin = ojetUtils.requireLocalFirst('mini-css-extract-plugin'); 16 | const webpack = ojetUtils.requireLocalFirst('webpack'); 17 | const configPaths = ojetUtils.getConfiguredPaths(); 18 | const isTypescriptApplication = ojetUtils.isTypescriptApplication(); 19 | 20 | // use polyfill for chai unless it's resolvable 21 | let chai = false; // eslint-disable-line 22 | try { 23 | chai = require.resolve('chai'); 24 | } catch (ex) {} // eslint-disable-line 25 | 26 | module.exports = { 27 | entry: webpackUtils.getEntryObject(), 28 | optimization: { 29 | concatenateModules: false, 30 | providedExports: false, 31 | usedExports: false, 32 | runtimeChunk: 'single', 33 | }, 34 | output: { 35 | path: path.resolve(configPaths.staging.web), 36 | clean: true, 37 | environment: { 38 | module: true, 39 | dynamicImport: true, 40 | } 41 | }, 42 | module: { 43 | rules: [{ 44 | sideEffects: true 45 | }, 46 | { 47 | test: /\.(png|jpg|jpeg|svg|gif|ico)$/i, 48 | type: 'asset', 49 | generator: { 50 | filename: `${configPaths.src.styles}/dynamicImages/[hash][ext][query]` 51 | } 52 | }, 53 | { 54 | test: /\.(css|sass|scss)$/i, 55 | use: [ 56 | MiniCssExtractPlugin.loader, 57 | 'css-loader', 58 | 'sass-loader', 59 | { 60 | loader: 'css-fix-url-loader', 61 | // There is no path to images/../../redwood/images/. 62 | // Enable webpack to resolve it as images/. 63 | options: { 64 | from: 'images/../../redwood/images', 65 | to: 'images', 66 | } 67 | }, 68 | { 69 | loader: 'css-fix-url-loader', 70 | // There is no 'images' folder under the created theme's folder. 71 | // Redirect the path to the alta's images subfolder. 72 | options: { 73 | from: 'images/animated-overlay.gif', 74 | to: `../../../alta/${ojetUtils.getJETVersion()}/common/images/animated-overlay.gif`, 75 | } 76 | } 77 | ] 78 | }, 79 | { 80 | test: /\.tsx?/, 81 | loader: 'ts-loader', 82 | exclude: /node_modules/, 83 | options: { 84 | getCustomTransformers: program => ({ 85 | before: [ 86 | // eslint-disable-next-line global-require 87 | require('./custom-tsc').metadataTransformer(program), 88 | // eslint-disable-next-line global-require 89 | require('./custom-tsc').decoratorTransformer(program) 90 | ] 91 | }) 92 | } 93 | }, 94 | { 95 | // disabling default webpack's JSON-handling for web components which use 96 | // text! to import *.json files 97 | test: resource => (/\.json$/i.test(resource) && webpackUtils.isWebComponent(resource)), 98 | type: 'javascript/auto' 99 | } 100 | ], 101 | }, 102 | resolve: { 103 | modules: [webpackUtils.localComponentsPath, webpackUtils.exchangeComponentsPath, 'node_modules'], 104 | extensions: ['.ts', '.tsx', '.js', '.css'], 105 | alias: { 106 | react: 'preact/compat', 107 | 'react-dom': 'preact/compat', 108 | ojdnd: '@oracle/oraclejet/dist/js/libs/dnd-polyfill/dnd-polyfill-1.0.2', 109 | signals: 'signals/dist/signals.js', 110 | touchr: '@oracle/oraclejet/dist/js/libs/touchr/touchr', 111 | 'jqueryui-amd': '@oracle/oraclejet/dist/js/libs/jquery/jqueryui-amd-1.14.1', 112 | ojs: '@oracle/oraclejet/dist/js/libs/oj/debug', 113 | ojtranslations: '@oracle/oraclejet/dist/js/libs/oj/resources', 114 | 'oj-c': '@oracle/oraclejet-core-pack/oj-c' 115 | }, 116 | fallback: { chai } 117 | }, 118 | resolveLoader: { 119 | modules: [ 120 | 'node_modules', 121 | path.join( 122 | webpackUtils.oracleJetDistPath, 123 | 'webpack-tools', 124 | 'loaders' 125 | ), 126 | ], 127 | alias: { 128 | ojL10n: 'ojL10n-loader', 129 | text: 'raw-loader?esModule=false', 130 | css: 'noop-loader', 131 | ojcss: 'noop-loader', 132 | 'ojs/ojcss': 'noop-loader', 133 | } 134 | }, 135 | plugins: [ 136 | new WebpackCopyPlugin({ 137 | patterns: webpackUtils.getCopyPluginPatterns() 138 | }), 139 | new HtmlWebpackPlugin({ 140 | template: path.join(configPaths.src.common, 'index.html'), 141 | inject: 'body' 142 | }), 143 | // This plugin sets options for the ojL10n-loader (in this case, just the locale name) 144 | new webpack.LoaderOptionsPlugin({ 145 | options: { 146 | ojL10nLoader: { 147 | locale: 'en-US', 148 | }, 149 | }, 150 | }), 151 | new webpack.ProvidePlugin({ 152 | $: 'jquery', 153 | jQuery: 'jquery', 154 | }), 155 | new WebpackRequireFixupPlugin({ 156 | ojModuleResources: { 157 | // The path to the root folder where the application-level (as opposed to relative) 158 | // ojModule/ views and viewModels are located 159 | root: webpackUtils.getRootPath(), 160 | 161 | // view settings for ojModule and 162 | view: { 163 | prefix: 'text!', 164 | // regular expression for locating all views under the root folder 165 | match: '^\\./views/.+\\.html$', 166 | }, 167 | // viewModel sttings for ojModule and 168 | viewModel: { 169 | // regular expression for locating all viewModels under the root folder 170 | match: isTypescriptApplication ? '^\\./viewModels/.+\\.ts$' : '^\\./viewModels/.+\\.js$', 171 | // Webpack search for lazy modules does not add '.js' extension automatically, 172 | // so we need to specify it explicitly 173 | addExtension: isTypescriptApplication ? '.ts' : '.js', 174 | }, 175 | }, 176 | // Point this setting to the root folder for the associated JET distribution 177 | // (could be a CDN). Used by the oj.Config.getResourceUri() call 178 | baseResourceUrl: `${configPaths.staging.web}/${configPaths.src.javascript}/libs/oj/${ojetUtils.getJETVersionV(ojetUtils.getJETVersion())}`, 179 | }), 180 | ], 181 | }; 182 | -------------------------------------------------------------------------------- /lib/webpack/webpack.development.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const ojetUtils = require('../util'); 8 | const common = require('./webpack.common.js'); 9 | 10 | const PreactRefreshPlugin = ojetUtils.requireLocalFirst('@prefresh/webpack'); 11 | const { 12 | merge 13 | } = ojetUtils.requireLocalFirst('webpack-merge'); 14 | const configPaths = ojetUtils.getConfiguredPaths(); 15 | const MiniCssExtractPlugin = ojetUtils.requireLocalFirst('mini-css-extract-plugin'); 16 | 17 | module.exports = merge(common, { 18 | mode: 'development', 19 | output: { 20 | filename: 'js/[name].bundle.js', 21 | clean: true 22 | }, 23 | devServer: { 24 | static: { 25 | directory: configPaths.staging.web 26 | }, 27 | client: { 28 | overlay: { 29 | errors: true, 30 | warnings: false, 31 | }, 32 | }, 33 | compress: true, 34 | port: 8000, 35 | open: true, 36 | hot: true 37 | }, 38 | plugins: [ 39 | new PreactRefreshPlugin(), 40 | new MiniCssExtractPlugin({ 41 | filename: `${configPaths.src.styles}/[name].css` 42 | }), 43 | ], 44 | }); 45 | -------------------------------------------------------------------------------- /lib/webpack/webpack.production.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const ojetUtils = require('../util'); 8 | const zlib = require('zlib'); 9 | const common = require('./webpack.common.js'); 10 | const path = require('path'); 11 | 12 | const MiniCssExtractPlugin = ojetUtils.requireLocalFirst('mini-css-extract-plugin'); 13 | 14 | const { 15 | merge 16 | } = ojetUtils.requireLocalFirst('webpack-merge'); 17 | const webpack = ojetUtils.requireLocalFirst('webpack'); 18 | const CompressionPlugin = ojetUtils.requireLocalFirst('compression-webpack-plugin'); 19 | const configPaths = ojetUtils.getConfiguredPaths(); 20 | const { 21 | CleanWebpackPlugin 22 | } = ojetUtils.requireLocalFirst('clean-webpack-plugin'); 23 | 24 | module.exports = merge(common, { 25 | mode: 'production', 26 | devtool: 'source-map', 27 | output: { 28 | filename: 'js/[name].[chunkhash].js', 29 | chunkFilename: 'js/[name].[chunkhash].js', 30 | path: path.resolve(configPaths.staging.web), 31 | clean: true 32 | }, 33 | plugins: [ 34 | new CompressionPlugin({ 35 | filename: '[path][base].br', 36 | algorithm: 'brotliCompress', 37 | test: /\.(js|css|html|svg)$/, 38 | compressionOptions: { 39 | params: { 40 | [zlib.constants.BROTLI_PARAM_QUALITY]: 11, 41 | }, 42 | }, 43 | threshold: 10240, 44 | minRatio: 0.8, 45 | deleteOriginalAssets: false, 46 | }), 47 | new webpack.optimize.ModuleConcatenationPlugin(), 48 | new CleanWebpackPlugin(), 49 | new MiniCssExtractPlugin({ 50 | filename: `${configPaths.src.styles}/[name].[contenthash].css` 51 | }) 52 | ], 53 | }); 54 | -------------------------------------------------------------------------------- /oraclejet-tooling.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | /** 8 | * # Tooling for Oracle JET apps 9 | * - - - 10 | * *This is the description. See more at [Oracle JET](http://www.oracle.com/jet) website.* 11 | */ 12 | 13 | 'use strict'; 14 | 15 | /** 16 | * Expose ojet object 17 | */ 18 | const ojet = module.exports; 19 | 20 | const CONSTANTS = require('./lib/constants'); 21 | 22 | /** 23 | * Expose libraries 24 | */ 25 | [ 26 | CONSTANTS.API_TASKS.ADD, 27 | CONSTANTS.API_TASKS.ADDSASS, 28 | 'build', 29 | 'clean', 30 | 'config', 31 | CONSTANTS.API_TASKS.CONFIGURE, 32 | CONSTANTS.API_TASKS.CREATE, 33 | CONSTANTS.API_TASKS.LIST, 34 | CONSTANTS.API_TASKS.ADDPCSS, 35 | CONSTANTS.API_TASKS.PUBLISH, 36 | CONSTANTS.API_TASKS.REMOVE, 37 | CONSTANTS.API_TASKS.SEARCH, 38 | CONSTANTS.API_TASKS.ADDTYPESCRIPT, 39 | CONSTANTS.API_TASKS.ADDJSDOC, 40 | CONSTANTS.API_TASKS.ADDPWA, 41 | CONSTANTS.API_TASKS.ADDWEBPACK, 42 | CONSTANTS.API_TASKS.ADDTESTING, 43 | 'serve', 44 | 'strip' 45 | ].forEach((name) => { 46 | ojet[name] = require('./lib/' + name); // eslint-disable-line 47 | }); 48 | 49 | // Instantiate Package class 50 | const PackageClass = require('./lib/' + CONSTANTS.API_TASKS.PACKAGE); 51 | const packageInstance = new PackageClass(); 52 | // Expose ojet.package() 53 | ojet[CONSTANTS.API_TASKS.PACKAGE] = packageInstance.package; 54 | 55 | /** 56 | * Expose other objects 57 | */ 58 | ojet.package.json = require('./package.json'); 59 | 60 | ojet.version = ojet.package.version; 61 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "@oracle/oraclejet-tooling", 3 | "version": "18.1.0", 4 | "license": "UPL-1.0", 5 | "description": "Programmatic API to build and serve Oracle JET web and mobile applications", 6 | "keywords": [ 7 | "oraclejet" 8 | ], 9 | "main": "oraclejet-tooling.js", 10 | "files": [ 11 | "RELEASENOTES.md", 12 | "README.md", 13 | "SECURITY.md", 14 | "LICENSE.txt", 15 | "THIRDPARTYLICENSE.txt", 16 | "hooks", 17 | "lib" 18 | ], 19 | "dependencies": { 20 | "acorn": "8.8.0", 21 | "acorn-jsx": "5.3.2", 22 | "eslint-visitor-keys": "1.3.0", 23 | "css-what": "~5.0.1", 24 | "fs-extra": "~8.1.0", 25 | "glob": "10.4.5", 26 | "lodash.mergewith": "~4.6.2", 27 | "requirejs": "~2.3.6", 28 | "terser": "~5.19.2", 29 | "chokidar": "3.6.0", 30 | "express": "4.21.2", 31 | "connect-livereload": "~0.6.0", 32 | "tiny-lr": "2.0.0", 33 | "serve-static": "1.16.2", 34 | "serve-index": "~1.9.1", 35 | "open": "6.4.0", 36 | "svgo": "3.3.2", 37 | "archiver": "7.0.1", 38 | "extract-zip": "~1.7.0", 39 | "form-data": "~2.5.1", 40 | "underscore": "~1.13.1" 41 | }, 42 | "overrides": { 43 | "glob": "10.4.5", 44 | "path-to-regexp": "0.1.12" 45 | }, 46 | "engines": { 47 | "node": ">=12.21.0" 48 | } 49 | } -------------------------------------------------------------------------------- /test/config.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const assert = require('assert'); 8 | const ojet = require('../oraclejet-tooling'); 9 | const _ = require('lodash'); 10 | 11 | describe('Config Test', () => { 12 | it('should init empty config', () => { 13 | ojet.config(); 14 | assert(_.isEmpty(ojet.config())); 15 | }); 16 | 17 | it('should init non-empty config', () => { 18 | ojet.config('name', 'test1'); 19 | assert(ojet.config('name') === 'test1'); 20 | }); 21 | 22 | it('should get config', () => { 23 | assert(ojet.config('name'), 'test1'); 24 | }); 25 | 26 | it('should get entire config', () => { 27 | assert(ojet.config(), { name: 'test1' }); 28 | }); 29 | 30 | it('should set config', () => { 31 | ojet.config('value', '123'); 32 | assert(ojet.config('value'), '123'); 33 | }); 34 | 35 | it('should overwrite config', () => { 36 | ojet.config('value', '1234'); 37 | assert(ojet.config('value'), '1234'); 38 | }); 39 | }); 40 | 41 | -------------------------------------------------------------------------------- /test/hook.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const assert = require('assert'); 8 | const hooks = require('../lib/hookRunner'); 9 | 10 | const hookList = [ 11 | 'after_app_create', 12 | 'after_app_restore', 13 | 'after_app_typescript', 14 | 'after_build', 15 | 'after_component_build', 16 | 'after_component_create', 17 | 'after_component_package', 18 | 'after_component_typescript', 19 | 'after_serve', 20 | 'after_watch', 21 | 'before_app_typescript', 22 | 'before_build', 23 | 'before_component_typescript', 24 | 'before_injection', 25 | 'before_component_optimize', 26 | 'before_component_package', 27 | 'before_optimize', 28 | 'before_release', 29 | 'before_release_build', 30 | 'before_serve', 31 | 'before_watch', 32 | 'before_webpack' 33 | ]; 34 | 35 | 36 | describe('Hooks Test', () => { 37 | before(() => { 38 | process.env.NODE_ENV = 'test'; 39 | }); 40 | 41 | hookList.forEach((element) => { 42 | it(`should have a ${element} hook`, () => { 43 | hooks(element, {platform: 'web', opts: {theme: 'alta'}, buildType: 'dev'}); 44 | assert(process.env, element); 45 | }); 46 | }); 47 | 48 | it('should not terminate invalid hooks', () => { 49 | assert.doesNotThrow(() => { 50 | hooks('before_build'); 51 | }); 52 | }); 53 | }); 54 | 55 | -------------------------------------------------------------------------------- /test/util.js: -------------------------------------------------------------------------------- 1 | /** 2 | Copyright (c) 2015, 2025, Oracle and/or its affiliates. 3 | Licensed under The Universal Permissive License (UPL), Version 1.0 4 | as shown at https://oss.oracle.com/licenses/upl/ 5 | 6 | */ 7 | const assert = require('assert'); 8 | const path = require('path'); 9 | const ojetUtil = require('../lib/util'); 10 | const _ = require('lodash'); 11 | 12 | const util = require('../lib/util'); 13 | 14 | describe('Util Test', () => { 15 | it('should have templatePath', () => { 16 | const template = util.templatePath(''); 17 | assert(template === path.resolve('../oraclejet-tooling')); 18 | }); 19 | 20 | it('should have destPath', () => { 21 | const template = util.destPath('test1'); 22 | assert(template === path.resolve('test1')); 23 | }); 24 | 25 | describe('Config Test', () => { 26 | it('should expect false for isCCaSassFile', () => { 27 | assert(ojetUtil.isCcaSassFile('testApp/staged-themes/alta/web/alta.css') === false); 28 | }); 29 | 30 | it('should expect true for isCCaSassFile', () => { 31 | assert(ojetUtil.isCcaSassFile('jet-composites/mytheme.css') == true); 32 | }); 33 | }); 34 | }); --------------------------------------------------------------------------------