Skip to content

Commit 7afd807

Browse files
authored
Merge pull request #32 from oslabs-beta/staging-faast
Add Next.js features by Fredo
2 parents a6c61d2 + 0e2c715 commit 7afd807

File tree

9 files changed

+435
-43
lines changed

9 files changed

+435
-43
lines changed

app/src/components/App.tsx

Lines changed: 20 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import initialState from '../context/initialState';
99
import reducer from '../reducers/componentReducer';
1010
import { getProjects } from '../helperFunctions/projectGetSave';
1111
import { saveProject } from '../helperFunctions/projectGetSave';
12-
import { loadInitData } from '../actions/actionCreators';
12+
1313
// import { Context, State } from '../interfaces/InterfacesNew';
1414

1515
// Intermediary component to wrap main App component with higher order provider components
@@ -20,27 +20,27 @@ export const App = (): JSX.Element => {
2020
const [state, dispatch] = useReducer(reducer, initialState);
2121

2222
// gets projects from DB for current user on mount
23-
useEffect(() => {
24-
// getProjects returns a promise which is thenable
25-
getProjects().then(project => {
26-
if (project) {
27-
// if user has project we run a dispatch to update state with received project
28-
dispatch({
29-
type: 'SET INITIAL STATE',
30-
payload: project
31-
});
32-
}
33-
});
34-
}, []);
23+
// useEffect(() => {
24+
// // getProjects returns a promise which is thenable
25+
// getProjects().then(project => {
26+
// if (project) {
27+
// // if user has project we run a dispatch to update state with received project
28+
// dispatch({
29+
// type: 'SET INITIAL STATE',
30+
// payload: project
31+
// });
32+
// }
33+
// });
34+
// }, []);
3535

3636
// saves project to DB whenever there are changes to the state via this canvas component
37-
useEffect(() => {
38-
console.log('useEffect in CanvasNew ran');
39-
// setTimeout is necessary so the saveProjects method does not fire and save an empty project before the initial getProjects in AppNew
40-
setTimeout(() => {
41-
saveProject(state);
42-
}, 1000);
43-
}, [state]);
37+
// useEffect(() => {
38+
// console.log('useEffect in CanvasNew ran');
39+
// // setTimeout is necessary so the saveProjects method does not fire and save an empty project before the initial getProjects in AppNew
40+
// setTimeout(() => {
41+
// saveProject(state);
42+
// }, 1000);
43+
// }, [state]);
4444

4545
return (
4646
<div className="app">

app/src/components/left/ComponentPanel.tsx

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -14,18 +14,19 @@ import { makeStyles } from '@material-ui/core/styles';
1414

1515
const useStyles = makeStyles({
1616
inputField: {
17-
17+
marginTop: '15px'
1818
},
1919
inputWrapper: {
20-
height: '110px',
20+
height: '115px',
2121
textAlign: 'center',
2222
display: 'flex',
2323
justifyContent: 'center',
24-
paddingLeft: '35px'
24+
paddingLeft: '35px',
25+
marginBottom: '15px'
2526
},
2627
panelWrapper: {
27-
marginTop: '35px',
28-
width: '100%'
28+
width: '100%',
29+
marginTop: '15px',
2930
},
3031
panelWrapperList: {
3132
maxHeight: '675px',
@@ -179,7 +180,7 @@ const ComponentPanel = (): JSX.Element => {
179180
>
180181
ADD
181182
</Button>
182-
{/* <FormControlLabel
183+
<FormControlLabel
183184
control={
184185
<Switch
185186
checked={isRoot}
@@ -189,7 +190,7 @@ const ComponentPanel = (): JSX.Element => {
189190
}
190191
className={classes.rootToggle}
191192
label="ROOT"
192-
/> */}
193+
/>
193194
</div>
194195
</div>
195196
<div className={classes.panelWrapperList}>

app/src/components/login/SignUp.tsx

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,5 @@
11
import React, { Component, useState, useEffect } from 'react';
22
import { LoginInt } from '../../interfaces/Interfaces';
3-
import { setLoginState } from '../../actions/actionCreators';
43
import {
54
Link as RouteLink,
65
withRouter,

app/src/containers/LeftContainer.tsx

Lines changed: 21 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -53,9 +53,11 @@ const LeftContainer = (): JSX.Element => {
5353
// genOption = 1 --> export an entire project w/ webpack, server, etc.
5454
const genOptions: string[] = [
5555
'Export components',
56-
'Export components with application files'
56+
'Export components with application files',
57+
'Export project as Next.js application'
5758
];
58-
const [genOption, setGenOption] = useState(1);
59+
// const [genOption, setGenOption] = useState(1);
60+
let genOption = 0;
5961
// state to keep track of whether there should be a modal
6062
const [modal, setModal] = useState(null);
6163

@@ -115,17 +117,27 @@ const LeftContainer = (): JSX.Element => {
115117
))}
116118
</List>
117119
);
118-
const chooseAppDir = () => window.api.chooseAppDir();
120+
// const chooseAppDir = () => {
121+
// console.log('CALLED CHOOSE APP DIR: ', genOption);
122+
// window.api.chooseAppDir();
123+
// };
124+
119125
// helper function called by showGenerateAppModal
120126
// this function will prompt the user to choose an app directory once they've chosen their export option
121127
const chooseGenOptions = (genOpt: number) => {
122128
// set export option: 0 --> export only components, 1 --> export full project
123-
console.log('in chooseGenOptions');
124-
setGenOption(genOpt);
125-
console.log('Gen option is ', genOpt);
129+
130+
// setGenOption(genOpt);
131+
genOption = genOpt;
132+
console.log('CALLED CHOOSE GEN OPTION: ', genOption);
133+
// closeModal
134+
// exportProject('/Users', 'NEW PROJECT', genOpt, state.components, state.rootComponents);
135+
// closeModal();
126136
// Choose app dir
127137
// window.api.chooseAppDir;
128-
chooseAppDir();
138+
139+
window.api.chooseAppDir();
140+
129141
// closeModal
130142

131143
closeModal();
@@ -135,8 +147,8 @@ const LeftContainer = (): JSX.Element => {
135147
// when a directory is chosen, the callback will export the project to the chosen folder
136148
// Note: this listener is imported from the main process via preload.js
137149
window.api.appDirChosen(path => {
138-
console.log('App dir chosen : ', path);
139-
exportProject(path, 'NEW PROJECT', genOption, state.components);
150+
console.log('CALLED APPDIRCHOSEN: ', genOption);
151+
exportProject(path, 'NEW PROJECT', genOption, state.components, state.rootComponents);
140152
});
141153

142154
setModal(
Lines changed: 136 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,136 @@
1+
import { format } from 'prettier';
2+
import { Component, State, ChildElement } from '../interfaces/InterfacesNew';
3+
import HTMLTypes from '../context/HTMLTypes';
4+
5+
//this is nearly identical to generateCode.ts but accounts for pages referencing imports in the components subfolder
6+
7+
// generate code based on the component heirarchy
8+
const generateUnformattedCode = (comps: Component[], componentId: number, rootComponents: number[]) => {
9+
const components = [...comps];
10+
// find the component that we're going to generate code for
11+
const currentComponent = components.find(elem => elem.id === componentId);
12+
// find the unique components that we need to import into this component file
13+
let imports: any = [];
14+
15+
const isRoot = rootComponents.includes(componentId);
16+
17+
// get metadata for each child
18+
const getEnrichedChildren = (currentComponent: Component | ChildElement) => {
19+
const enrichedChildren = currentComponent.children.map((elem: any) => {
20+
const child = { ...elem };
21+
if (child.type === 'Component') {
22+
const referencedComponent = components.find(
23+
elem => elem.id === child.typeId
24+
);
25+
if (!imports.includes(referencedComponent.name))
26+
imports.push(referencedComponent.name);
27+
child['name'] = referencedComponent.name;
28+
return child;
29+
} else if (child.type === 'HTML Element') {
30+
const referencedHTML = HTMLTypes.find(elem => elem.id === child.typeId);
31+
child['tag'] = referencedHTML.tag;
32+
if (referencedHTML.tag === 'div') {
33+
child.children = getEnrichedChildren(child);
34+
}
35+
return child;
36+
}
37+
});
38+
return enrichedChildren;
39+
};
40+
41+
const writeNestedElements = (enrichedChildren: any) => {
42+
return `${enrichedChildren
43+
.map((child: any) => {
44+
if (child.type === 'Component') {
45+
return `<${child.name}${formatStyles(child.style)} />`;
46+
} else if (child.type === 'HTML Element') {
47+
if (child.tag === 'img') {
48+
return `<${child.tag} src=""${formatStyles(child.style)} />`;
49+
} else if (child.tag === 'a') {
50+
return `<${child.tag} href=""${formatStyles(child.style)}>[LINK]</${child.tag}>`;
51+
} else if (child.tag === 'div') {
52+
return `<${child.tag}${formatStyles(
53+
child.style
54+
)}>${writeNestedElements(child.children)}</${child.tag}>`;
55+
} else if (child.tag === 'h1') {
56+
return `<${child.tag}${formatStyles(child.style)}>HEADER 1</${child.tag}>`;
57+
} else if (child.tag === 'h2') {
58+
return `<${child.tag}${formatStyles(child.style)}>HEADER 2</${child.tag}>`;
59+
} else if (child.tag === 'form') {
60+
return `<${child.tag}${formatStyles(child.style)}>FORM</${child.tag}>`;
61+
} else if (child.tag === 'p') {
62+
return `<${child.tag}${formatStyles(child.style)}>Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat.</${child.tag}>`;
63+
} else if (child.tag === 'li') {
64+
return `<ul${formatStyles(child.style)}><li>item 1</li>
65+
<li>item 2</li>
66+
<li>item 3</li></ul>`;
67+
} else if (child.tag === 'button'){
68+
return `<${child.tag}${formatStyles(child.style)}>BUTTON</${child.tag}>`;
69+
} else {
70+
return `<${child.tag}${formatStyles(child.style)}></${child.tag}>`;
71+
}
72+
}
73+
})
74+
.join('\n')}`;
75+
};
76+
77+
const formatStyles = (styleObj: any) => {
78+
if (Object.keys(styleObj).length === 0) return ``;
79+
const formattedStyles = [];
80+
for (let i in styleObj) {
81+
const styleString = i + ': ' + "'" + styleObj[i] + "'";
82+
formattedStyles.push(styleString);
83+
}
84+
return ' style={{' + formattedStyles.join(',') + '}}';
85+
};
86+
87+
const enrichedChildren: any = getEnrichedChildren(currentComponent);
88+
89+
console.log(enrichedChildren);
90+
91+
// import statements differ between root (pages) and regular components (components)
92+
const importsMapped = imports.map((comp: string) => {
93+
return isRoot ? `import ${comp} from '../components/${comp}'`: `import ${comp} from './${comp}'`;
94+
})
95+
.join('\n')
96+
97+
return `
98+
import React, { useState } from 'react';
99+
100+
${importsMapped}
101+
102+
const ${currentComponent.name} = (props) => {
103+
104+
const [value, setValue] = useState("INITIAL VALUE");
105+
106+
return (
107+
<div className="${currentComponent.name}" style={props.style}>
108+
${writeNestedElements(enrichedChildren)}
109+
</div>
110+
);
111+
}
112+
113+
export default ${currentComponent.name};
114+
`;
115+
};
116+
117+
// formats code with prettier linter
118+
const formatCode = (code: string) => {
119+
return window.api.formatCode(code);
120+
// return format(code, {
121+
// singleQuote: true,
122+
// trailingComma: 'es5',
123+
// bracketSpacing: true,
124+
// jsxBracketSameLine: true,
125+
// parser: 'babel'
126+
// });
127+
};
128+
129+
// generate code based on component heirarchy and then return the rendered code
130+
const generateNextCode = (components: Component[], componentId: number, rootComponents: number[]) => {
131+
const code = generateUnformattedCode(components, componentId, rootComponents);
132+
return formatCode(code);
133+
};
134+
135+
export default generateNextCode;
136+

app/src/utils/createApplication.util.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,27 @@
22
// const fs = __non_webpack_require__('fs');
33
// let fs;
44
import createFiles from './createFiles.util';
5+
import { Component, State, ChildElement } from '../interfaces/InterfacesNew';
6+
7+
const camelToKebab= (camel:string) => {
8+
return camel.replace(/([a-z0-9]|(?=[A-Z]))([A-Z])/g, '$1-$2').toLowerCase();
9+
};
10+
11+
const compToCSS = (component: Component) => {
12+
const name = component.name;
13+
const styleObj = component.style;
14+
let cssClass = `
15+
.${name} {
16+
`;
17+
Object.keys(styleObj).forEach(property => {
18+
let cssStyle = `${camelToKebab(property)}: ${styleObj[property]};
19+
`;
20+
cssClass += cssStyle;
21+
})
22+
cssClass += `}
23+
`;
24+
return cssClass;
25+
}
526

627
function createIndexHtml(path, appName) {
728
let dir = path;
@@ -64,9 +85,9 @@ ReactDOM.render(<App />, document.getElementById('root'));
6485
});
6586
};
6687

67-
export const createDefaultCSS = (path, appName) => {
88+
export const createDefaultCSS = (path, appName, components) => {
6889
const filePath = `${path}/${appName}/src/default.css`;
69-
const data = `
90+
let data = `
7091
#root div {
7192
box-sizing: border-box;
7293
width: 100%;
@@ -76,6 +97,10 @@ export const createDefaultCSS = (path, appName) => {
7697
font-family: Helvetica, Arial;
7798
}
7899
`;
100+
components.forEach(comp => {
101+
data += compToCSS(comp);
102+
})
103+
79104
window.api.writeFile(filePath, data, err => {
80105
if (err) {
81106
console.log('default.css error:', err.message);
@@ -322,13 +347,13 @@ async function createApplicationUtil({
322347
}: {
323348
path: string;
324349
appName: string;
325-
components: any;
350+
components: Component[];
326351
}) {
327352
console.log('in the createApplication util');
328353

329354
await createIndexHtml(path, appName);
330355
await createIndexTsx(path, appName);
331-
await createDefaultCSS(path, appName);
356+
await createDefaultCSS(path, appName, components);
332357
await createPackage(path, appName);
333358
await createWebpack(path, appName);
334359
await createBabel(path, appName);

0 commit comments

Comments
 (0)