diff --git a/.circleci/api-comment.js b/.circleci/api-comment.js index 262d449daa3..fb30bca8c69 100644 --- a/.circleci/api-comment.js +++ b/.circleci/api-comment.js @@ -53,7 +53,13 @@ async function run() { if (pr != null) { let commentId = await findDifferComment(pr); - let diffs = fs.readFileSync('/tmp/dist/ts-diff.txt'); + let diffs; + try { + diffs = fs.readFileSync('/tmp/dist/ts-diff.txt'); + } catch (e) { + console.log('No TS Diff output to run on.') + return; + } if (diffs.length > 0) { if (commentId != null) { // delete existing comment diff --git a/.circleci/config.yml b/.circleci/config.yml index bb0d6195bd8..5665d79bf6b 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -27,6 +27,14 @@ executors: CACHE_VERSION: v1 working_directory: ~/react-spectrum + rsp-xlarge-nodeupdate: + docker: + - image: cimg/node:16.18.0 + resource_class: xlarge + environment: + CACHE_VERSION: v1 + working_directory: ~/react-spectrum + jobs: install: executor: rsp-large @@ -206,6 +214,20 @@ jobs: - store_artifacts: path: ~/junit + + test-esm: + executor: rsp-xlarge-nodeupdate + steps: + - restore_cache: + key: react-spectrum-{{ .Environment.CACHE_VERSION }}-{{ .Environment.CIRCLE_SHA1 }} + + - run: + name: test + command: | + make build + yarn lerna run prepublishOnly + node --loader ./scripts/esm-support/loader.mjs ./scripts/esm-support/testESM.mjs + lint: executor: rsp steps: @@ -451,6 +473,9 @@ workflows: - test-17: requires: - install-17 + - test-esm: + requires: + - install - lint: requires: - install @@ -506,6 +531,7 @@ workflows: - test-16 - test-ssr-17 - test-17 + - test-esm - storybook - storybook-16 - storybook-17 diff --git a/Makefile b/Makefile index 0a13d2dd222..be03b0a2d52 100644 --- a/Makefile +++ b/Makefile @@ -83,6 +83,12 @@ publish-nightly: build build: parcel build packages/@react-{spectrum,aria,stately}/*/ packages/@internationalized/{message,string,date,number}/ --no-optimize + yarn lerna run prepublishOnly + for pkg in packages/@react-{spectrum,aria,stately}/*/ packages/@internationalized/{message,string,date,number}/ packages/@adobe/react-spectrum/ packages/react-aria/ packages/react-stately/; \ + do cp $$pkg/dist/module.js $$pkg/dist/import.mjs; \ + done + sed -i.bak s/\.js/\.mjs/ packages/@react-aria/i18n/dist/import.mjs + rm packages/@react-aria/i18n/dist/import.mjs.bak website: yarn build:docs --public-url /reactspectrum/$$(git rev-parse HEAD)/docs --dist-dir dist/$$(git rev-parse HEAD)/docs diff --git a/babel-esm.config.json b/babel-esm.config.json new file mode 100644 index 00000000000..cb8c7529066 --- /dev/null +++ b/babel-esm.config.json @@ -0,0 +1,61 @@ +{ + "presets": [ + "@babel/preset-typescript", + "@babel/preset-react", + ["@babel/preset-env", + { + "loose": true, + "modules": false + } + ] + ], + "env": { + "storybook": { + "presets": [ + [ + "@babel/preset-env", + { + "loose": true, + "targets": { + "esmodules": true + } + } + ] + ] + }, + "cover": { + "plugins": [ + "istanbul" + ] + }, + "production": { + "plugins": [ + [ + "react-remove-properties", + { + "properties": [ + "data-testid" + ] + } + ] + ] + } + }, + "plugins": [ + [ + "@babel/plugin-transform-runtime", + { + "version": "^7.6.2" + } + ], + [ + "@babel/plugin-proposal-decorators", + { + "legacy": true + } + ], + "transform-glob-import", + "babel-plugin-macros" + ], + "sourceType": "unambiguous" +} diff --git a/bin/imports.js b/bin/imports.js index 7569f5595f0..5e0fd9e64a1 100644 --- a/bin/imports.js +++ b/bin/imports.js @@ -16,73 +16,78 @@ const fs = require('fs'); const Module = require('module'); const substrings = ['-', '+']; -module.exports = function (context) { - let processNode = (node) => { - if (!node.source || node.importKind === 'type') { - return; - } - - let source = node.source.value.replace(/^[a-z]+:/, ''); - if (source.startsWith('.') || Module.builtinModules.includes(source)) { - return; - } - - // Split the import specifier on slashes. If it starts with an @ then it's - // a scoped package, otherwise just take the first part. - let parts = source.split('/'); - let pkgName = source.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]; - - // Search for a package.json starting from the current filename - let pkgPath = findUp.sync('package.json', {cwd: path.dirname(context.getFilename())}); - if (!pkgPath) { - return; - } - - let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); - - // The only dev dependency should be spectrum-css. - if (exists(pkg.devDependencies, pkgName) && pkgName === '@adobe/spectrum-css-temp') { - return; - } - - if (!exists(pkg.dependencies, pkgName) && !exists(pkg.peerDependencies, pkgName)) { - context.report({ - node, - message: `Missing dependency on ${pkgName}.`, - fix(fixer) { - // Attempt to find a package in the monorepo. If the dep is for an external library, - // then we cannot auto fix it because we don't know the version to add. - let depPath = __dirname + '/../packages/' + pkgName + '/package.json'; - if (!fs.existsSync(depPath)) { - return; +module.exports = { + meta: { + fixable: 'code' + }, + create: function (context) { + let processNode = (node) => { + if (!node.source || node.importKind === 'type') { + return; + } + + let source = node.source.value.replace(/^[a-z]+:/, ''); + if (source.startsWith('.') || Module.builtinModules.includes(source)) { + return; + } + + // Split the import specifier on slashes. If it starts with an @ then it's + // a scoped package, otherwise just take the first part. + let parts = source.split('/'); + let pkgName = source.startsWith('@') ? parts.slice(0, 2).join('/') : parts[0]; + + // Search for a package.json starting from the current filename + let pkgPath = findUp.sync('package.json', {cwd: path.dirname(context.getFilename())}); + if (!pkgPath) { + return; + } + + let pkg = JSON.parse(fs.readFileSync(pkgPath, 'utf8')); + + // The only dev dependency should be spectrum-css. + if (exists(pkg.devDependencies, pkgName) && pkgName === '@adobe/spectrum-css-temp') { + return; + } + + if (!exists(pkg.dependencies, pkgName) && !exists(pkg.peerDependencies, pkgName) && pkgName !== pkg.name) { + context.report({ + node, + message: `Missing dependency on ${pkgName}.`, + fix(fixer) { + // Attempt to find a package in the monorepo. If the dep is for an external library, + // then we cannot auto fix it because we don't know the version to add. + let depPath = __dirname + '/../packages/' + pkgName + '/package.json'; + if (!fs.existsSync(depPath)) { + return; + } + + let depPkg = JSON.parse(fs.readFileSync(depPath, 'utf8')); + let pkgVersion = substrings.some(v => depPkg.version.includes(v)) ? depPkg.version : `^${depPkg.version}`; + + if (pkgName === '@react-spectrum/provider') { + pkg.peerDependencies = insertObject(pkg.peerDependencies, pkgName, pkgVersion); + } else { + pkg.dependencies = insertObject(pkg.dependencies, pkgName, pkgVersion); + } + + fs.writeFileSync(pkgPath, JSON.stringify(pkg, false, 2) + '\n'); + + // Fake fix so eslint doesn't show the error. + return { + range: [0, 0], + text: '' + }; } - - let depPkg = JSON.parse(fs.readFileSync(depPath, 'utf8')); - let pkgVersion = substrings.some(v => depPkg.version.includes(v)) ? depPkg.version : `^${depPkg.version}`; - - if (pkgName === '@react-spectrum/provider') { - pkg.peerDependencies = insertObject(pkg.peerDependencies, pkgName, pkgVersion); - } else { - pkg.dependencies = insertObject(pkg.dependencies, pkgName, pkgVersion); - } - - fs.writeFileSync(pkgPath, JSON.stringify(pkg, false, 2) + '\n'); - - // Fake fix so eslint doesn't show the error. - return { - range: [0, 0], - text: '' - }; - } - }); - } - }; - - return { - ImportDeclaration: processNode, - ExportNamedDeclaration: processNode, - ExportAllDeclaration: processNode - }; + }); + } + }; + + return { + ImportDeclaration: processNode, + ExportNamedDeclaration: processNode, + ExportAllDeclaration: processNode + }; + } }; function exists(deps, name) { diff --git a/examples/rsp-next-ts/next.config.js b/examples/rsp-next-ts/next.config.mjs similarity index 98% rename from examples/rsp-next-ts/next.config.js rename to examples/rsp-next-ts/next.config.mjs index 3bc91f3b24d..aed16861c6f 100644 --- a/examples/rsp-next-ts/next.config.js +++ b/examples/rsp-next-ts/next.config.mjs @@ -1,4 +1,4 @@ -module.exports = { +export default { transpilePackages: [ "@adobe/react-spectrum", "@react-spectrum/actiongroup", diff --git a/examples/rsp-next-ts/package.json b/examples/rsp-next-ts/package.json index d1526ea5424..937bc041921 100644 --- a/examples/rsp-next-ts/package.json +++ b/examples/rsp-next-ts/package.json @@ -10,6 +10,7 @@ "start": "next start", "lint": "next lint" }, + "type": "module", "dependencies": { "@adobe/react-spectrum": "^3.22.0", "@react-spectrum/color": "^3.0.0-beta.16", diff --git a/examples/rsp-webpack-4/.babelrc b/examples/rsp-webpack-4/.babelrc new file mode 100644 index 00000000000..0dce8c0afe5 --- /dev/null +++ b/examples/rsp-webpack-4/.babelrc @@ -0,0 +1,3 @@ +{ + "presets": ["@babel/env", "@babel/preset-react"] +} diff --git a/examples/rsp-webpack-4/jest.config.js b/examples/rsp-webpack-4/jest.config.js new file mode 100644 index 00000000000..5dfb1591f16 --- /dev/null +++ b/examples/rsp-webpack-4/jest.config.js @@ -0,0 +1,5 @@ +module.exports = { + moduleNameMapper: { + '\\.(css|styl)$': 'identity-obj-proxy' + } +}; diff --git a/examples/rsp-webpack-4/package.json b/examples/rsp-webpack-4/package.json new file mode 100644 index 00000000000..68b4f7798c9 --- /dev/null +++ b/examples/rsp-webpack-4/package.json @@ -0,0 +1,34 @@ +{ + "name": "rsp-cra-18-webpack-4", + "version": "1.0.0", + "description": "test esm with webpack 4", + "main": "src/index.jsx", + "scripts": { + "build": "webpack --mode production", + "start": "webpack-dev-server --mode development --open", + "test": "test" + }, + "private": true, + "workspaces": [ + "../../packages/*/*" + ], + "dependencies": { + "@adobe/react-spectrum": "^3.24.1", + "@spectrum-icons/workflow": "^4.0.6", + "react": "^18.2.0", + "react-dom": "^18.2.0" + }, + "devDependencies": { + "@babel/core": "^7.1.0", + "@babel/cli": "^7.1.0", + "@babel/preset-env": "^7.1.0", + "@babel/preset-react": "^7.0.0", + "webpack": "4.19.1", + "webpack-cli": "3.1.1", + "webpack-dev-server": "3.1.8", + "style-loader": "0.23.0", + "css-loader": "1.0.0", + "babel-loader": "8.0.2", + "jest": "^26" + } +} diff --git a/examples/rsp-webpack-4/src/App.css b/examples/rsp-webpack-4/src/App.css new file mode 100644 index 00000000000..b6b369d5d05 --- /dev/null +++ b/examples/rsp-webpack-4/src/App.css @@ -0,0 +1,22 @@ +body{ + height: 100%; +} + +.no-bullets{ + list-style-type: none; + padding: 0px; +} + +#root{ + padding: 0; + margin: 0; + height: 100%; +} + +html { + height: 100%; +} + +.content-padding{ + padding: 50px; +} \ No newline at end of file diff --git a/examples/rsp-webpack-4/src/App.js b/examples/rsp-webpack-4/src/App.js new file mode 100644 index 00000000000..165489e8d80 --- /dev/null +++ b/examples/rsp-webpack-4/src/App.js @@ -0,0 +1,22 @@ +import './App.css'; +import {Provider, defaultTheme} from '@adobe/react-spectrum' +import Lighting from './Lighting'; +import {useState} from 'react' +import BodyContent from './BodyContent'; + +function App() { + let [selected, setSelection] = useState(false); + + return ( + +
+ + +
+
+ ); +} + +export default App; diff --git a/examples/rsp-webpack-4/src/BodyContent.js b/examples/rsp-webpack-4/src/BodyContent.js new file mode 100644 index 00000000000..8b7581a696d --- /dev/null +++ b/examples/rsp-webpack-4/src/BodyContent.js @@ -0,0 +1,106 @@ +import {useState, useRef} from "react"; +import {Item, TabList, TabPanels, Tabs} from '@adobe/react-spectrum' +import TodoList from './TodoList'; +import JournalList from './JournalList'; + + +function BodyContent(){ + + //states for the To-Do list + const [list, setList] = useState([]); + const [value, setValue] = useState(''); + const [completed, setCompleted] = useState([]); + const count = useRef(0); + + //states for journal entries + const [rating, setRating] = useState(''); + const [entryList, setEntryList] = useState([]); + const [description, setDescription] = useState(''); + const countJournals = useRef(0); + + const options = [ + {id: "Bad", name: "Bad"}, + {id: "Okay", name: "Okay"}, + {id: "Good", name: "Good"}, + {id: "Great", name: "Great"} + ] + + //functions for the To-Do list + function handleSubmitToDo(e){ + e.preventDefault() + + if (value.length > 0){ + setList(prevListArray => { + return [ + ...prevListArray, + {id: count.current, task: value}] + }) + + count.current = count.current + 1; + } + setValue(""); //clears text field on submit + } + + function updateCompleted(complete){ + setCompleted(prevListArray => { + return [ + ...prevListArray, + {id: prevListArray.length, task: complete}] + }); + } + + function clearCompleted(){ + setCompleted(() => { + return []; + }) + } + + //functions for journal entries + function handleSubmitJournals(e){ + e.preventDefault() + + countJournals.current = countJournals.current + 1; //used to determine key for each item in the entryList array + + setEntryList(prevListArray => { + return [ + ...prevListArray, + {rate: rating, description: description, id: countJournals.current} + ] + }) + + setValue('') //clears the text area when submitted + } + + return( + + + + To-do List + Daily Journal + + + + + + + + + + + ) +} + +export default BodyContent; diff --git a/examples/rsp-webpack-4/src/Completed.js b/examples/rsp-webpack-4/src/Completed.js new file mode 100644 index 00000000000..d36268fb0f5 --- /dev/null +++ b/examples/rsp-webpack-4/src/Completed.js @@ -0,0 +1,37 @@ +import Delete from '@spectrum-icons/workflow/Delete'; +import {AlertDialog, DialogTrigger, ActionButton} from '@adobe/react-spectrum' +import {Checkbox} from '@adobe/react-spectrum' +import {Flex} from '@adobe/react-spectrum' + +function Completed(props){ + + const elements = props.completed.map(item => ( + {item.task} + )) + + let alertCancel = () => alert('Cancel button pressed.'); + + return ( + + {elements} + + + + + + Are you sure you want to delete the completed tasks? + + + + + ) +} + +export default Completed; diff --git a/examples/rsp-webpack-4/src/JournalEntries.js b/examples/rsp-webpack-4/src/JournalEntries.js new file mode 100644 index 00000000000..b76dea74452 --- /dev/null +++ b/examples/rsp-webpack-4/src/JournalEntries.js @@ -0,0 +1,23 @@ +import {Flex, Divider} from '@adobe/react-spectrum' + +function JournalEntries(props){ + + const element = props.list.map(item => ( +
  • + +

    Your day was: {item.rate}

    +

    {item.description}

    +
  • + + )) + + return ( + + + + ) +} + +export default JournalEntries; diff --git a/examples/rsp-webpack-4/src/JournalList.js b/examples/rsp-webpack-4/src/JournalList.js new file mode 100644 index 00000000000..9e5b2cf546d --- /dev/null +++ b/examples/rsp-webpack-4/src/JournalList.js @@ -0,0 +1,32 @@ +import AddCircle from '@spectrum-icons/workflow/AddCircle'; +import {Flex, Text, Button, Form, TextArea, Picker, Item, Divider} from '@adobe/react-spectrum' +import JournalEntries from './JournalEntries' + +function JournalList(props){ + return( + <> +
    + + props.setRating(selected)} + > + {(item) => {item.name}} + +