chore(): React Build Scripts (#19501)

This commit is contained in:
Ely Lucas
2019-10-07 10:37:20 -06:00
committed by GitHub
parent aed2dba5aa
commit 8706ecf9c3
26 changed files with 1629 additions and 1273 deletions

View File

@ -108,6 +108,58 @@ jobs:
paths:
- "*"
build-react:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react
- run:
command: npm run build
working_directory: /tmp/workspace/packages/react
- persist_to_workspace:
root: /tmp/workspace
paths:
- "*"
build-react-router:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm install
working_directory: /tmp/workspace/packages/react-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react-router
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link @ionic/react
working_directory: /tmp/workspace/packages/react-router
- run:
command: npm run build
working_directory: /tmp/workspace/packages/react-router
- persist_to_workspace:
root: /tmp/workspace
paths:
- "*"
test-core-clean-build:
<<: *defaults
steps:
@ -181,6 +233,64 @@ jobs:
command: npm run lint
working_directory: /tmp/workspace/angular
test-react-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/react
test-react-router-lint:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: npm run lint
working_directory: /tmp/workspace/packages/react-router
test-react-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react
- run:
command: npm run test.spec
working_directory: /tmp/workspace/packages/react
test-react-router-spec:
<<: *defaults
steps:
- checkout
- attach_workspace:
at: /tmp/workspace
- run:
command: sudo npm link
working_directory: /tmp/workspace/core
- run:
command: sudo npm link @ionic/core
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link
working_directory: /tmp/workspace/packages/react
- run:
command: sudo npm link @ionic/react
working_directory: /tmp/workspace/packages/react-router
- run:
command: npm run test.spec
working_directory: /tmp/workspace/packages/react-router
test-angular-e2e:
<<: *defaults
steps:
@ -227,6 +337,18 @@ workflows:
requires: [build-core]
- build-angular-server:
requires: [build-angular]
- build-react:
requires: [build-core]
- build-react-router:
requires: [build-core, build-react]
- test-react-lint:
requires: [build-react]
- test-react-router-lint:
requires: [build-react-router]
- test-react-spec:
requires: [build-react]
- test-react-router-spec:
requires: [build-react-router]
- test-angular-lint:
requires: [build-angular]
- test-angular-e2e:

View File

@ -11,6 +11,8 @@ const packages = [
'core',
'docs',
'angular',
'packages/react',
'packages/react-router'
];
function readPkg(project) {
@ -36,7 +38,8 @@ function checkGit(tasks) {
tasks.push(
{
title: 'Check current branch',
task: () => execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
task: () =>
execa.stdout('git', ['symbolic-ref', '--short', 'HEAD']).then(branch => {
if (branch.indexOf('release') === -1 && branch.indexOf('hotfix') === -1) {
throw new Error(`Must be on a "release" or "hotfix" branch.`);
}
@ -44,7 +47,8 @@ function checkGit(tasks) {
},
{
title: 'Check local working tree',
task: () => execa.stdout('git', ['status', '--porcelain']).then(status => {
task: () =>
execa.stdout('git', ['status', '--porcelain']).then(status => {
if (status !== '') {
throw new Error(`Unclean working tree. Commit or stash changes first.`);
}
@ -52,7 +56,8 @@ function checkGit(tasks) {
},
{
title: 'Check remote history',
task: () => execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
task: () =>
execa.stdout('git', ['rev-list', '--count', '--left-only', '@{u}...HEAD']).then(result => {
if (result !== '0') {
throw new Error(`Remote history differs. Please pull changes.`);
}
@ -61,8 +66,19 @@ function checkGit(tasks) {
);
}
const isValidVersion = input => Boolean(semver.valid(input));
function checkTestDist(tasks) {
tasks.push({
title: 'Check dist folders for required files',
task: () =>
execa.stdout('node', ['.scripts/test-dist.js']).then(status => {
if (status.indexOf('✅ test.dist') === -1) {
throw new Error(`Test Dist did not find some required files`);
}
})
});
}
const isValidVersion = input => Boolean(semver.valid(input));
function preparePackage(tasks, package, version, install) {
const projectRoot = projectPath(package);
@ -74,7 +90,9 @@ function preparePackage(tasks, package, version, install) {
title: `${pkg.name}: validate new version`,
task: () => {
if (!isVersionGreater(pkg.version, version)) {
throw new Error(`New version \`${version}\` should be higher than current version \`${pkg.version}\``);
throw new Error(
`New version \`${version}\` should be higher than current version \`${pkg.version}\``
);
}
}
});
@ -82,7 +100,7 @@ function preparePackage(tasks, package, version, install) {
projectTasks.push({
title: `${pkg.name}: install npm dependencies`,
task: async () => {
await fs.remove(path.join(projectRoot, 'node_modules'))
await fs.remove(path.join(projectRoot, 'node_modules'));
await execa('npm', ['i'], { cwd: projectRoot });
}
});
@ -95,6 +113,13 @@ function preparePackage(tasks, package, version, install) {
title: `${pkg.name}: npm link @ionic/core`,
task: () => execa('npm', ['link', '@ionic/core'], { cwd: projectRoot })
});
if (package === 'packages/react-router') {
projectTasks.push({
title: `${pkg.name}: npm link @ionic/react`,
task: () => execa('npm', ['link', '@ionic/react'], { cwd: projectRoot })
});
}
}
if (version) {
@ -105,7 +130,7 @@ function preparePackage(tasks, package, version, install) {
projectTasks.push({
title: `${pkg.name}: update ionic/core dep to ${version}`,
task: () => {
updateDependency(pkg, "@ionic/core", version);
updateDependency(pkg, '@ionic/core', version);
writePkg(package, pkg);
}
});
@ -134,7 +159,6 @@ function preparePackage(tasks, package, version, install) {
});
}
function prepareDevPackage(tasks, package, version) {
const projectRoot = projectPath(package);
const pkg = readPkg(package);
@ -152,7 +176,7 @@ function prepareDevPackage(tasks, package, version) {
projectTasks.push({
title: `${pkg.name}: update ionic/core dep to ${version}`,
task: () => {
updateDependency(pkg, "@ionic/core", version);
updateDependency(pkg, '@ionic/core', version);
writePkg(package, pkg);
}
});
@ -181,8 +205,7 @@ function updatePackageVersions(tasks, packages, version) {
packages.forEach(package => {
updatePackageVersion(tasks, package, version);
tasks.push(
{
tasks.push({
title: `${package} update @ionic/core dependency, if present ${tc.dim(`(${version})`)}`,
task: async () => {
if (package !== 'core') {
@ -190,24 +213,30 @@ function updatePackageVersions(tasks, packages, version) {
updateDependency(pkg, '@ionic/core', version);
writePkg(package, pkg);
}
},
}
)
});
if (package === 'packages/react-router') {
tasks.push({
title: `${package} update @ionic/react dependency, if present ${tc.dim(`(${version})`)}`,
task: async () => {
const pkg = readPkg(package);
updateDependency(pkg, '@ionic/react', version);
writePkg(package, pkg);
}
});
}
});
}
function updatePackageVersion(tasks, package, version) {
const projectRoot = projectPath(package);
tasks.push(
{
tasks.push({
title: `${package}: update package.json ${tc.dim(`(${version})`)}`,
task: async () => {
await execa('npm', ['version', version], { cwd: projectRoot });
}
}
);
});
}
function publishPackages(tasks, packages, version, tag = 'latest') {
@ -237,7 +266,7 @@ function publishPackages(tasks, packages, version, tag = 'latest') {
title: `${package}: publish to ${tag} tag`,
task: async () => {
await execa('npm', ['publish', '--tag', tag], { cwd: projectRoot });
},
}
});
});
}
@ -261,11 +290,12 @@ function isVersionGreater(oldVersion, newVersion) {
function copyCDNLoader(tasks, version) {
tasks.push({
title: `Copy CDN loader`,
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') }),
task: () => execa('node', ['copy-cdn-loader.js', version], { cwd: path.join(rootDir, 'core', 'scripts') })
});
}
module.exports = {
checkTestDist,
checkGit,
isValidVersion,
isVersionGreater,
@ -281,5 +311,5 @@ module.exports = {
updateDependency,
updatePackageVersion,
updatePackageVersions,
writePkg,
writePkg
};

View File

@ -107,13 +107,14 @@ async function preparePackages(packages, version, install) {
});
// add update package.json of each project
packages.forEach(package => {
common.updatePackageVersion(tasks, package, version);
});
common.updatePackageVersions(tasks, packages, version);
// generate changelog
generateChangeLog(tasks);
// check dist folders
common.checkTestDist(tasks);
// update core readme with version number
updateCoreReadme(tasks, version);
common.copyCDNLoader(tasks, version);

82
.scripts/test-dist.js Normal file
View File

@ -0,0 +1,82 @@
const path = require('path');
const fs = require('fs');
// Test dist build:
// Double-triple check all the packages
// and files are good to go before publishing
[
// core
{
files: ['../core/dist/index.js', '../core/dist/ionic/index.esm.js']
},
// angular
{
files: ['../angular/dist/fesm5.cjs.js']
},
// react
{
files: ['../packages/react/dist/index.js']
},
// react-router
{
files: ['../packages/react-router/dist/index.js']
}
].forEach(testPackage);
function testPackage(testPkg) {
if (testPkg.packageJson) {
const pkgDir = path.dirname(testPkg.packageJson);
const pkgJson = require(testPkg.packageJson);
if (!pkgJson.name) {
throw new Error('missing package.json name: ' + testPkg.packageJson);
}
if (!pkgJson.main) {
throw new Error('missing package.json main: ' + testPkg.packageJson);
}
const pkgPath = path.join(pkgDir, pkgJson.main);
const pkgImport = require(pkgPath);
if (testPkg.files) {
if (!Array.isArray(pkgJson.files)) {
throw new Error(testPkg.packageJson + ' missing "files" property');
}
testPkg.files.forEach(testPkgFile => {
if (!pkgJson.files.includes(testPkgFile)) {
throw new Error(testPkg.packageJson + ' missing file ' + testPkgFile);
}
const filePath = path.join(__dirname, pkgDir, testPkgFile);
fs.accessSync(filePath);
});
}
if (pkgJson.module) {
const moduleIndex = path.join(__dirname, pkgDir, pkgJson.module);
fs.accessSync(moduleIndex);
}
if (pkgJson.types) {
const pkgTypes = path.join(__dirname, pkgDir, pkgJson.types);
fs.accessSync(pkgTypes);
}
if (testPkg.exports) {
testPkg.exports.forEach(exportName => {
const m = pkgImport[exportName];
if (!m) {
throw new Error('export "' + exportName + '" not found in: ' + testPkg.packageJson);
}
});
}
} else if (testPkg.files) {
testPkg.files.forEach(file => {
const filePath = path.join(__dirname, file);
fs.statSync(filePath);
});
}
}
console.log(`✅ test.dist`);

View File

@ -1,5 +1,3 @@
global.crypto = require('@trust/webcrypto');
window.matchMedia = window.matchMedia || function() {
return {
matches : false,

View File

@ -24,8 +24,10 @@
"clean": "rm -rf dist dist-transpiled",
"compile": "npm run tsc && rollup -c",
"release": "np --any-branch --no-cleanup",
"lint": "tslint --project .",
"lint.fix": "tslint --project . --fix",
"tsc": "tsc -p .",
"test": "jest"
"test.spec": "jest --ci"
},
"main": "dist/index.js",
"module": "dist/index.esm.js",
@ -34,7 +36,10 @@
"dist/"
],
"dependencies": {
"tslib": "*"
"tslib": "*",
"tslint": "^5.20.0",
"tslint-ionic-rules": "0.0.21",
"tslint-react": "^4.1.0"
},
"peerDependencies": {
"@ionic/core": "^4.10.0",

View File

@ -1,4 +1,4 @@
import { match, RouteProps } from 'react-router-dom';
import { RouteProps, match } from 'react-router-dom';
export interface IonRouteData {
match: match<{ tab: string }> | null;

View File

@ -3,9 +3,11 @@ import { NavContext, NavContextState } from '@ionic/react';
import { Location as HistoryLocation, UnregisterCallback } from 'history';
import React from 'react';
import { RouteComponentProps } from 'react-router-dom';
import { StackManager } from './StackManager';
import { generateId } from '../utils';
import { LocationHistory } from '../utils/LocationHistory'
import { LocationHistory } from '../utils/LocationHistory';
import { StackManager } from './StackManager';
import { ViewItem } from './ViewItem';
import { ViewStack } from './ViewStacks';
@ -13,12 +15,11 @@ interface NavManagerProps extends RouteComponentProps {
findViewInfoByLocation: (location: HistoryLocation) => { view?: ViewItem, viewStack?: ViewStack };
findViewInfoById: (id: string) => { view?: ViewItem, viewStack?: ViewStack };
getActiveIonPage: () => { view?: ViewItem, viewStack?: ViewStack };
};
interface NavManagerState extends NavContextState {};
}
export class NavManager extends React.Component<NavManagerProps, NavManagerState> {
export class NavManager extends React.Component<NavManagerProps, NavContextState> {
listenUnregisterCallback: UnregisterCallback;
listenUnregisterCallback: UnregisterCallback | undefined;
locationHistory: LocationHistory = new LocationHistory();
constructor(props: NavManagerProps) {
@ -32,13 +33,13 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
getStackManager: this.getStackManager.bind(this),
getPageManager: this.getPageManager.bind(this),
currentPath: this.props.location.pathname,
registerIonPage: () => {} //overridden in View for each IonPage
}
registerIonPage: () => { return; } // overridden in View for each IonPage
};
this.listenUnregisterCallback = this.props.history.listen((location: HistoryLocation) => {
this.setState({
currentPath: location.pathname
})
});
this.locationHistory.add(location);
});
@ -69,10 +70,14 @@ export class NavManager extends React.Component<NavManagerProps, NavManagerState
this.props.history.replace(enteringView.routeData.match.url, { direction: 'back' });
}
} else {
defaultHref && this.props.history.replace(defaultHref, { direction: 'back' });
if (defaultHref) {
this.props.history.replace(defaultHref, { direction: 'back' });
}
}
} else {
defaultHref && this.props.history.replace(defaultHref, { direction: 'back' });
if (defaultHref) {
this.props.history.replace(defaultHref, { direction: 'back' });
}
}
}

View File

@ -1,5 +1,6 @@
import React, { ReactNode } from 'react';
import { NavDirection } from '@ionic/core';
import React, { ReactNode } from 'react';
import { ViewStacks } from './ViewStacks';
export interface RouteManagerContextState {
@ -15,11 +16,11 @@ export const RouteManagerContext = /*@__PURE__*/React.createContext<RouteManager
viewStacks: new ViewStacks(),
syncView: () => { navContextNotFoundError(); },
hideView: () => { navContextNotFoundError(); },
setupIonRouter: () => { return Promise.reject(navContextNotFoundError()) },
setupIonRouter: () => Promise.reject(navContextNotFoundError()),
removeViewStack: () => { navContextNotFoundError(); },
transitionView: () => { navContextNotFoundError(); }
});
function navContextNotFoundError() {
console.error('IonReactRouter not found, did you add it to the app?')
console.error('IonReactRouter not found, did you add it to the app?');
}

View File

@ -2,27 +2,26 @@ import { NavDirection } from '@ionic/core';
import { RouterDirection } from '@ionic/react';
import { Action as HistoryAction, Location as HistoryLocation, UnregisterCallback } from 'history';
import React from 'react';
import { BrowserRouter, BrowserRouterProps, matchPath, RouteComponentProps, withRouter } from 'react-router-dom';
import { BrowserRouter, BrowserRouterProps, RouteComponentProps, matchPath, withRouter } from 'react-router-dom';
import { generateId } from '../utils';
import { IonRouteData } from './IonRouteData';
import { NavManager } from './NavManager';
import { RouteManagerContext, RouteManagerContextState } from './RouteManagerContext';
import { ViewItem } from './ViewItem';
import { ViewStacks, ViewStack } from './ViewStacks';
interface RouteManagerProps extends RouteComponentProps { }
import { ViewStack, ViewStacks } from './ViewStacks';
interface RouteManagerState extends RouteManagerContextState {
location?: HistoryLocation,
action?: HistoryAction
location?: HistoryLocation;
action?: HistoryAction;
}
class RouteManager extends React.Component<RouteManagerProps, RouteManagerState> {
class RouteManager extends React.Component<RouteComponentProps, RouteManagerState> {
listenUnregisterCallback: UnregisterCallback | undefined;
activeIonPageId?: string;
constructor(props: RouteManagerProps) {
constructor(props: RouteComponentProps) {
super(props);
this.listenUnregisterCallback = this.props.history.listen(this.historyChange.bind(this));
this.state = {
@ -35,7 +34,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
};
}
componentDidUpdate(_prevProps: RouteManagerProps, prevState: RouteManagerState) {
componentDidUpdate(_prevProps: RouteComponentProps, prevState: RouteManagerState) {
// Trigger a page change if the location or action is different
if (this.state.location && prevState.location !== this.state.location || prevState.action !== this.state.action) {
this.setActiveView(this.state.location!, this.state.action!);
@ -61,7 +60,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
this.setState({
location,
action
})
});
}
setActiveView(location: HistoryLocation, action: HistoryAction) {
@ -77,8 +76,6 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
}
leavingView = viewStacks.findViewInfoById(this.activeIonPageId).view;
if (enteringView) {
if (enteringView.isIonRoute) {
enteringView.show = true;
enteringView.mount = true;
@ -107,7 +104,6 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
enteringView.mount = true;
enteringView.routeData.match = match!;
}
}
});
if (leavingView) {
@ -120,7 +116,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
this.setState({
viewStacks
}, () => {
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId)
const { view: enteringView, viewStack } = this.state.viewStacks.findViewInfoById(this.activeIonPageId);
if (enteringView && viewStack) {
const enteringEl = enteringView.ionPageElement ? enteringView.ionPageElement : undefined;
const leavingEl = leavingView && leavingView.ionPageElement ? leavingView.ionPageElement : undefined;
@ -132,7 +128,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
enteringEl!,
leavingEl!,
viewStack.routerOutlet,
navDirection)
navDirection);
} else if (leavingEl) {
leavingEl.classList.add('ion-page-hidden');
leavingEl.setAttribute('aria-hidden', 'true');
@ -142,7 +138,9 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
}
componentWillUnmount() {
this.listenUnregisterCallback && this.listenUnregisterCallback();
if (this.listenUnregisterCallback) {
this.listenUnregisterCallback();
}
}
async setupIonRouter(id: string, children: any, routerOutlet: HTMLIonRouterOutletElement) {
@ -172,12 +170,12 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
match,
childProps: child.props
},
route: route,
route,
mount: true,
show: !!match,
isIonRoute: false
};
if (!!match && view.isIonRoute) {
if (match && view.isIonRoute) {
activeId = viewId;
}
return view;
@ -186,9 +184,9 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
async registerViewStack(stack: string, activeId: string | undefined, stackItems: ViewItem[], routerOutlet: HTMLIonRouterOutletElement, _location: HistoryLocation) {
return new Promise((resolve) => {
this.setState((prevState) => {
const prevViewStacks = Object.assign(new ViewStacks, prevState.viewStacks);
return new Promise(resolve => {
this.setState(prevState => {
const prevViewStacks = Object.assign(new ViewStacks(), prevState.viewStacks);
const newStack: ViewStack = {
id: stack,
views: stackItems,
@ -205,7 +203,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
resolve();
});
});
};
}
removeViewStack(stack: string) {
const viewStacks = Object.assign(new ViewStacks(), this.state.viewStacks);
@ -216,7 +214,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
}
syncView(page: HTMLElement, viewId: string) {
this.setState((state) => {
this.setState(state => {
const viewStacks = Object.assign(new ViewStacks(), state.viewStacks);
const { view } = viewStacks.findViewInfoById(viewId);
@ -226,14 +224,14 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
return {
viewStacks
}
};
}, () => {
this.setActiveView(this.state.location || this.props.location, this.state.action!);
})
});
}
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet: HTMLIonRouterOutletElement, direction?: NavDirection) {
transitionView(enteringEl: HTMLElement, leavingEl: HTMLElement, ionRouterOutlet?: HTMLIonRouterOutletElement, direction?: NavDirection) {
/**
* Super hacky workaround to make sure ionRouterOutlet is available
* since transitionView might be called before IonRouterOutlet is fully mounted
@ -256,7 +254,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
await ionRouterOuter.commit(enteringEl, leavingEl, {
deepWait: true,
duration: direction === undefined ? 0 : undefined,
direction: direction,
direction,
showGoBack: direction === 'forward',
progressAnimation: false
});
@ -271,7 +269,8 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
render() {
return (
<RouteManagerContext.Provider value={this.state}>
<NavManager {...this.props}
<NavManager
{...this.props}
findViewInfoById={(id: string) => this.state.viewStacks.findViewInfoById(id)}
findViewInfoByLocation={(location: HistoryLocation) => this.state.viewStacks.findViewInfoByLocation(location)}
getActiveIonPage={() => this.state.viewStacks.findViewInfoById(this.activeIonPageId)}
@ -281,7 +280,7 @@ class RouteManager extends React.Component<RouteManagerProps, RouteManagerState>
</RouteManagerContext.Provider>
);
}
};
}
const RouteManagerWithRouter = withRouter(RouteManager);
RouteManagerWithRouter.displayName = 'RouteManager';

View File

@ -1,17 +1,17 @@
import React from 'react';
import { generateId, isDevMode } from '../utils';
import { View } from './View';
import { ViewTransitionManager } from './ViewTransitionManager';
import { RouteManagerContext } from './RouteManagerContext';
import { View } from './View';
import { ViewItem } from './ViewItem';
import { ViewTransitionManager } from './ViewTransitionManager';
type StackManagerProps = {
interface StackManagerProps {
id?: string;
};
}
type StackManagerState = {}
export class StackManager extends React.Component<StackManagerProps, StackManagerState> {
export class StackManager extends React.Component<StackManagerProps, {}> {
routerOutletEl: React.RefObject<HTMLIonRouterOutletElement> = React.createRef();
context!: React.ContextType<typeof RouteManagerContext>;
id: string;
@ -52,7 +52,7 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
const views = (viewStack || { views: [] }).views.filter(x => x.show);
const ionRouterOutlet = React.Children.only(this.props.children) as React.ReactElement;
const childElements = views.map((view) => {
const childElements = views.map(view => {
return (
<ViewTransitionManager
id={view.id}
@ -72,15 +72,14 @@ export class StackManager extends React.Component<StackManagerProps, StackManage
const elementProps: any = {
ref: this.routerOutletEl
}
};
if (isDevMode()) {
elementProps['data-stack-id'] = this.id
elementProps['data-stack-id'] = this.id;
}
const routerOutletChild = React.cloneElement(ionRouterOutlet, elementProps, childElements);
return routerOutletChild;
}

View File

@ -1,21 +1,21 @@
import React from 'react';
import { IonLifeCycleContext, NavContext } from '@ionic/react';
import { ViewItem } from './ViewItem';
import { Route, Redirect } from 'react-router-dom';
import React from 'react';
import { Redirect, Route } from 'react-router-dom';
import { isDevMode } from '../utils';
import { ViewItem } from './ViewItem';
interface ViewProps extends React.HTMLAttributes<HTMLElement> {
onViewSync: (page: HTMLElement, viewId: string) => void;
onHideView: (viewId: string) => void;
view: ViewItem;
};
interface StackViewState { }
}
/**
* The View component helps manage the IonPage's lifecycle and registration
*/
export class View extends React.Component<ViewProps, StackViewState> {
export class View extends React.Component<ViewProps, {}> {
context!: React.ContextType<typeof IonLifeCycleContext>;
ionPage?: HTMLElement;
@ -78,7 +78,7 @@ export class View extends React.Component<ViewProps, StackViewState> {
const newProvider = {
...value,
registerIonPage: this.registerIonPage.bind(this)
}
};
return (
<NavContext.Provider value={newProvider}>
{this.props.children}

View File

@ -1,14 +1,11 @@
import * as React from 'react';
import { deprecationWarning } from '../utils';
interface ViewManagerProps { }
interface ViewManagerState { }
export class ViewManager extends React.Component<ViewManagerProps, ViewManagerState> {
export class ViewManager extends React.Component<{}, {}> {
componentDidMount() {
deprecationWarning('As of @ionic/react RC2, ViewManager is no longer needed and can be removed. This component is now deprecated will be removed from @ionic/react final.')
deprecationWarning('As of @ionic/react RC2, ViewManager is no longer needed and can be removed. This component is now deprecated will be removed from @ionic/react final.');
}
render() {

View File

@ -1,19 +1,20 @@
import { Location as HistoryLocation } from 'history';
import { ViewItem } from './ViewItem';
import { IonRouteData } from './IonRouteData';
import { matchPath } from 'react-router-dom';
import { IonRouteData } from './IonRouteData';
import { ViewItem } from './ViewItem';
export interface ViewStack {
id: string;
routerOutlet: HTMLIonRouterOutletElement;
views: ViewItem[]
views: ViewItem[];
}
/**
* The holistic view of all the Routes configured for an application inside of an IonRouterOutlet.
*/
export class ViewStacks {
private viewStacks: { [key: string]: ViewStack } = {};
private viewStacks: { [key: string]: ViewStack | undefined } = {};
get(key: string) {
return this.viewStacks[key];
@ -31,12 +32,12 @@ export class ViewStacks {
delete this.viewStacks[key];
}
findViewInfoByLocation(location: HistoryLocation, key?: string) {
findViewInfoByLocation(location: HistoryLocation, viewKey?: string) {
let view: ViewItem<IonRouteData> | undefined;
let match: IonRouteData["match"] | null | undefined;
let match: IonRouteData['match'] | null | undefined;
let viewStack: ViewStack | undefined;
if (key) {
viewStack = this.viewStacks[key];
if (viewKey) {
viewStack = this.viewStacks[viewKey];
if (viewStack) {
viewStack.views.some(matchView);
}
@ -44,7 +45,7 @@ export class ViewStacks {
const keys = this.getKeys();
keys.some(key => {
viewStack = this.viewStacks[key];
return viewStack.views.some(matchView);
return viewStack!.views.some(matchView);
});
}
@ -57,7 +58,7 @@ export class ViewStacks {
path: v.routeData.childProps.path || v.routeData.childProps.from,
component: v.routeData.childProps.component
};
match = matchPath(location.pathname, matchProps)
match = matchPath(location.pathname, matchProps);
if (match) {
view = v;
return true;
@ -67,13 +68,13 @@ export class ViewStacks {
}
findViewInfoById(id: string = '') {
findViewInfoById(id = '') {
let view: ViewItem<IonRouteData> | undefined;
let viewStack: ViewStack | undefined;
const keys = this.getKeys();
keys.some(key => {
const vs = this.viewStacks[key];
view = vs.views.find(x => x.id === id);
view = vs!.views.find(x => x.id === id);
if (view) {
viewStack = vs;
return true;
@ -88,13 +89,12 @@ export class ViewStacks {
const keys = this.getKeys();
keys.forEach(key => {
const viewStack = this.viewStacks[key];
viewStack.views.forEach(view => {
viewStack!.views.forEach(view => {
if (!view.routeData.match && !view.isIonRoute) {
view.show = false;
view.mount = false;
}
})
})
});
});
}
}

View File

@ -1,5 +1,6 @@
import { DefaultIonLifeCycleContext, IonLifeCycleContext } from '@ionic/react';
import React from 'react';
import { IonLifeCycleContext, DefaultIonLifeCycleContext } from '@ionic/react';
import { RouteManagerContext } from './RouteManagerContext';
interface ViewTransitionManagerProps {
@ -20,7 +21,7 @@ export class ViewTransitionManager extends React.Component<ViewTransitionManager
context!: React.ContextType<typeof RouteManagerContext>;
constructor(props: ViewTransitionManagerProps) {
super(props)
super(props);
this.state = {
show: true
};
@ -52,7 +53,7 @@ export class ViewTransitionManager extends React.Component<ViewTransitionManager
<IonLifeCycleContext.Provider value={this.ionLifeCycleContext}>
{show && this.props.children}
</IonLifeCycleContext.Provider>
)
);
}
static get contextType() {

View File

@ -1 +1 @@
export * from './ReactRouter'
export * from './ReactRouter';

View File

@ -0,0 +1,14 @@
import { isDevMode } from '../dev';
describe('isDevMode', () => {
it('by default, should return false since we are in test', () => {
const isDev = isDevMode();
expect(isDev).toBeFalsy();
});
it('when in dev mode, should return true', () => {
process.env.NODE_ENV = 'development';
const isDev = isDevMode();
expect(isDev).toBeTruthy()
});
});

View File

@ -1,3 +1,32 @@
{
"extends": "tslint-ionic-rules"
"extends": ["tslint-ionic-rules/strict", "tslint-react"],
"linterOptions": {
"exclude": [
"**/*.spec.ts",
"**/*.spec.tsx"
]
},
"rules": {
"no-conditional-assignment": false,
"no-non-null-assertion": false,
"no-unnecessary-type-assertion": false,
"no-import-side-effect": false,
"trailing-comma": false,
"no-null-keyword": false,
"no-console": false,
"no-unbound-method": false,
"no-floating-promises": false,
"no-invalid-template-strings": true,
"ban-export-const-enum": true,
"only-arrow-functions": false,
"strict-boolean-conditions": [true, "allow-null-union", "allow-undefined-union", "allow-boolean-or-undefined", "allow-string"],
"jsx-key": false,
"jsx-self-close": false,
"jsx-curly-spacing": [true, "never"],
"jsx-boolean-value": [true, "never"],
"jsx-no-bind": false,
"jsx-no-lambda": false,
"jsx-no-multiline-js": false,
"jsx-wrap-multiline": false
}
}

View File

@ -28,6 +28,7 @@
"lint.fix": "tslint --project . --fix",
"tsc": "tsc -p .",
"copy": "node scripts/copy.js",
"test.spec": "jest --ci",
"test.treeshake": "node scripts/treeshaking.js dist/index.esm.js"
},
"main": "dist/index.js",

View File

@ -1,7 +1,7 @@
import React from 'react';
import { IonTabs, IonTabButton, IonLabel, IonIcon, IonTabBar} from '../index';
import { render, cleanup } from 'react-testing-library';
import { IonRouterOutlet } from '../proxies';
import { IonRouterOutlet } from '../IonRouterOutlet';
afterEach(cleanup)

View File

@ -22,7 +22,7 @@ describe('createComponent - events', () => {
test('should add custom events', () => {
const FakeIonFocus = jest.fn((e) => e);
const IonInput = createReactComponent<JSX.IonInput>('ion-input');
const IonInput = createReactComponent<JSX.IonInput, HTMLIonInputElement>('ion-input');
const { getByText } = render(
<IonInput onIonFocus={FakeIonFocus}>

View File

@ -69,8 +69,8 @@ export const isCoveredByReact = (eventNameSuffix: string, doc: Document = docume
return isSupported;
};
export const syncEvent = (node: Element, eventName: string, newEventHandler: (e: Event) => any) => {
const eventStore = (node as any).__events || ((node as any).__events = {});
export const syncEvent = (node: Element & {__events?: {[key: string]: ((e: Event) => any) | undefined}}, eventName: string, newEventHandler?: (e: Event) => any) => {
const eventStore = node.__events || (node.__events = {});
const oldEventHandler = eventStore[eventName];
// Remove old listener so they don't double up.

File diff suppressed because it is too large Load Diff

View File

@ -10,7 +10,7 @@
"@types/react-dom": "16.8.5",
"react": "^16.8.6",
"react-dom": "^16.8.6",
"react-scripts": "^3.1.0",
"react-scripts": "^3.1.2",
"serve": "^11.1.0",
"typescript": "3.5.3"
},

View File

@ -19,7 +19,7 @@
"no-invalid-template-strings": true,
"ban-export-const-enum": true,
"only-arrow-functions": true,
"strict-boolean-conditions": [true, "allow-null-union", "allow-undefined-union", "allow-boolean-or-undefined", "allow-string"],
"jsx-key": false,
"jsx-self-close": false,
"jsx-curly-spacing": [true, "never"],