Repository: teleporthq/teleport-code-generators
Pull request #334 (2019-08-05 09:07:06)

refactor components generated in own folder

codecov[bot]: # [Codecov](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=h1) Report
> Merging [#334](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=desc) into [master](https://codecov.io/gh/teleporthq/teleport-code-generators/commit/bc6ce26a287a7d7e0a054bbc387a0c651db6b622?src=pr&el=desc) will **increase** coverage by `0.02%`.
> The diff coverage is `92.3%`.

[![Impacted file tree graph](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/graphs/tree.svg?width=650&token=LI2WPEFm1R&height=150&src=pr)](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=tree)

```diff
@@            Coverage Diff             @@
##           master     #334      +/-   ##
==========================================
+ Coverage   75.42%   75.44%   +0.02%     
==========================================
  Files         101      101              
  Lines        3365     3368       +3     
  Branches      711      712       +1     
==========================================
+ Hits         2538     2541       +3     
  Misses        800      800              
  Partials       27       27
```


| [Impacted Files](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=tree) | Coverage Δ | |
|---|---|---|
| [...kages/teleport-project-generator-nuxt/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcHJvamVjdC1nZW5lcmF0b3ItbnV4dC9zcmMvaW5kZXgudHM=) | `100% <ø> (ø)` | :arrow_up: |
| [...kages/teleport-project-generator-next/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcHJvamVjdC1nZW5lcmF0b3ItbmV4dC9zcmMvaW5kZXgudHM=) | `100% <ø> (ø)` | :arrow_up: |
| [packages/teleport-project-generator/src/utils.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcHJvamVjdC1nZW5lcmF0b3Ivc3JjL3V0aWxzLnRz) | `98.87% <100%> (ø)` | :arrow_up: |
| [...ges/teleport-plugin-react-app-routing/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcGx1Z2luLXJlYWN0LWFwcC1yb3V0aW5nL3NyYy9pbmRleC50cw==) | `91.17% <100%> (ø)` | :arrow_up: |
| [...ges/teleport-project-generator-preact/src/index.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcHJvamVjdC1nZW5lcmF0b3ItcHJlYWN0L3NyYy9pbmRleC50cw==) | `100% <100%> (ø)` | :arrow_up: |
| [...es/teleport-project-generator/src/file-handlers.ts](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334/diff?src=pr&el=tree#diff-cGFja2FnZXMvdGVsZXBvcnQtcHJvamVjdC1nZW5lcmF0b3Ivc3JjL2ZpbGUtaGFuZGxlcnMudHM=) | `94.35% <75%> (+0.09%)` | :arrow_up: |

------

[Continue to review full report at Codecov](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=continue).
> **Legend** - [Click here to learn more](https://docs.codecov.io/docs/codecov-delta)
> `Δ = absolute <relative> (impact)`, `ø = not affected`, `? = missing data`
> Powered by [Codecov](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=footer). Last update [bc6ce26...537cb84](https://codecov.io/gh/teleporthq/teleport-code-generators/pull/334?src=pr&el=lastupdated). Read the [comment docs](https://docs.codecov.io/docs/pull-request-comments).


Patch:
diff --git a/packages/teleport-component-generator-preact/src/index.ts b/packages/teleport-component-generator-preact/src/index.ts
index e32c67e9..468776a9 100644
--- a/packages/teleport-component-generator-preact/src/index.ts
+++ b/packages/teleport-component-generator-preact/src/index.ts
@@ -13,6 +13,7 @@ import { ComponentGenerator, Mapping } from '@teleporthq/teleport-types'
 
 const cssModulesPlugin = createCSSModulesPlugin({
   classAttributeName: 'class',
+  moduleExtension: false,
 })
 
 export const createPreactComponentGenerator = (mapping: Mapping = {}): ComponentGenerator => {
diff --git a/packages/teleport-component-generator/src/index.ts b/packages/teleport-component-generator/src/index.ts
index acc21af7..851fe9c0 100644
--- a/packages/teleport-component-generator/src/index.ts
+++ b/packages/teleport-component-generator/src/index.ts
@@ -3,8 +3,13 @@ import AssemblyLine from './assembly-line'
 import Builder from './builder'
 import Resolver from './resolver'
 
-import { getComponentFileName } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
+import {
+  getComponentFileName,
+  getStyleFileName,
+  getTemplateFileName,
+} from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 
+import { FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
 import {
   ChunkDefinition,
   ComponentGenerator,
@@ -71,7 +76,9 @@ export const createComponentGenerator = (
     })
 
     const fileName = getComponentFileName(resolvedUIDL)
-    const files = fileBundler(fileName, codeChunks)
+    const styleFileName = getStyleFileName(resolvedUIDL)
+    const templateFileName = getTemplateFileName(resolvedUIDL)
+    const files = fileBundler(codeChunks, fileName, styleFileName, templateFileName)
 
     return {
       files,
@@ -95,7 +102,7 @@ export const createComponentGenerator = (
       codeChunks = processor(codeChunks)
     })
 
-    return fileBundler(fileName, codeChunks)
+    return fileBundler(codeChunks, fileName)
   }
 
   const addPostProcessor = (fn: PostProcessor) => {
@@ -114,12 +121,36 @@ export const createComponentGenerator = (
 
 export default createComponentGenerator()
 
-const fileBundler = (fileName: string, codeChunks: Record<string, string>) => {
+const fileBundler = (
+  codeChunks: Record<string, string>,
+  fileName: string,
+  styleFileName?: string,
+  templateFileName?: string
+) => {
   return Object.keys(codeChunks).map((fileType) => {
     return {
-      name: fileName,
+      name: getFileName(fileType, fileName, styleFileName, templateFileName),
       fileType,
       content: codeChunks[fileType],
     }
   })
 }
+
+// Based on the file type we selected the file name associated.
+// This is mostly used by project generators when a component is exported in its own folder
+const getFileName = (
+  fileType: string,
+  fileName: string,
+  styleFileName: string,
+  templateFileName: string
+) => {
+  if (fileType === FILE_TYPE.CSS || fileType === FILE_TYPE.CSSMODULE) {
+    return styleFileName || fileName
+  }
+
+  if (fileType === FILE_TYPE.HTML) {
+    return templateFileName || fileName
+  }
+
+  return fileName
+}
diff --git a/packages/teleport-plugin-react-app-routing/src/index.ts b/packages/teleport-plugin-react-app-routing/src/index.ts
index 9b60e87c..6a4c81ee 100644
--- a/packages/teleport-plugin-react-app-routing/src/index.ts
+++ b/packages/teleport-plugin-react-app-routing/src/index.ts
@@ -21,6 +21,7 @@ interface AppRoutingComponentConfig {
   componentChunkName: string
   domRenderChunkName: string
   importChunkName: string
+  flavor: 'preact' | 'react'
 }
 
 export const createPlugin: ComponentPluginFactory<AppRoutingComponentConfig> = (config) => {
@@ -28,16 +29,13 @@ export const createPlugin: ComponentPluginFactory<AppRoutingComponentConfig> = (
     importChunkName = 'import-local',
     componentChunkName = 'app-router-component',
     domRenderChunkName = 'app-router-export',
+    flavor = 'react',
   } = config || {}
 
   const reactAppRoutingComponentPlugin: ComponentPlugin = async (structure) => {
     const { uidl, dependencies, options } = structure
-    // @ts-ignore-next-line
-    const { createFolderForEachComponent, flavour } = options.meta || {
-      createFolderForEachComponent: false,
-    }
 
-    if (flavour === 'preact') {
+    if (flavor === 'preact') {
       registerPreactRouterDeps(dependencies)
     } else {
       registerReactRouterDeps(dependencies)
@@ -46,25 +44,38 @@ export const createPlugin: ComponentPluginFactory<AppRoutingComponentConfig> = (
     const { stateDefinitions = {} } = uidl
 
     const routes = extractRoutes(uidl)
+    const strategy = options.strategy
     const pageDependencyPrefix = options.localDependenciesPrefix || './'
 
     const routeJSXDefinitions = routes.map((conditionalNode) => {
       const { value: routeKey } = conditionalNode.content
 
-      const { fileName, componentName, path } = extractPageMetadata(
+      const { fileName: pageName, componentName, path } = extractPageMetadata(
         stateDefinitions.route,
         routeKey.toString()
       )
 
+      /* If pages are exported in their own folder and in custom file names.
+         Import statements must then be:
+
+         import Home from '../pages/home/component'
+
+         so the `/component` suffix is computed below.
+      */
+      const pageStrategyOptions = (strategy && strategy.pages.options) || {}
+      const pageComponentSuffix = pageStrategyOptions.createFolderForEachComponent
+        ? '/' + (pageStrategyOptions.customComponentFileName || 'index')
+        : ''
+
       dependencies[componentName] = {
         type: 'local',
-        path: `${pageDependencyPrefix}${fileName}`,
+        path: `${pageDependencyPrefix}${pageName}${pageComponentSuffix}`,
       }
 
-      return constructRouteJSX(flavour, componentName, path)
+      return constructRouteJSX(flavor, componentName, path)
     })
 
-    const rootRouterTag = createRouteRouterTag(flavour, routeJSXDefinitions)
+    const rootRouterTag = createRouteRouterTag(flavor, routeJSXDefinitions)
 
     const pureComponent = createFunctionalComponent(uidl.name, rootRouterTag)
 
@@ -76,7 +87,7 @@ export const createPlugin: ComponentPluginFactory<AppRoutingComponentConfig> = (
       linkAfter: [importChunkName],
     })
 
-    if (flavour === 'preact') {
+    if (flavor === 'preact') {
       const exportJSXApp = createDefaultExport('App')
 
       structure.chunks.push({
diff --git a/packages/teleport-plugin-react-css-modules/src/index.ts b/packages/teleport-plugin-react-css-modules/src/index.ts
index 81239fb6..5f9cf6a1 100644
--- a/packages/teleport-plugin-react-css-modules/src/index.ts
+++ b/packages/teleport-plugin-react-css-modules/src/index.ts
@@ -11,6 +11,7 @@ import {
   splitDynamicAndStaticStyles,
   cleanupNestedStyles,
   transformDynamicStyles,
+  getStyleFileName,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 import {
   createCSSClass,
@@ -35,7 +36,7 @@ const defaultConfigProps = {
   styleChunkName: 'css-modules',
   styleObjectImportName: 'styles',
   camelCaseClassNames: false,
-  moduleExtension: 'false',
+  moduleExtension: false,
   classAttributeName: 'className',
 }
 
@@ -54,7 +55,6 @@ export const createPlugin: ComponentPluginFactory<CSSModulesConfig> = (config =
 
   const cssModulesPlugin: ComponentPlugin = async (structure) => {
     const { uidl, chunks, dependencies } = structure
-    const { name, meta } = uidl
 
     const componentChunk = chunks.filter((chunk) => chunk.name === componentChunkName)[0]
 
@@ -76,13 +76,16 @@ export const createPlugin: ComponentPluginFactory<CSSModulesConfig> = (config =
 
         if (Object.keys(staticStyles).length > 0) {
           const className = camelCaseToDashCase(key)
-          const classNameInJS = dashCaseToCamelCase(className)
+          const jsFriendlyClassName = dashCaseToCamelCase(className)
 
           cssClasses.push(createCSSClass(className, getContentOfStyleObject(staticStyles)))
 
-          const classReferenceIdentifier = camelCaseClassNames
-            ? `styles.${classNameInJS}`
-            : `styles['${className}']`
+          // When the className is equal to the jsFriendlyClassName, it can be safely addressed with `styles.<className>`
+          const classNameIsJSFriendly = className === jsFriendlyClassName
+          const classReferenceIdentifier =
+            camelCaseClassNames || classNameIsJSFriendly
+              ? `styles.${jsFriendlyClassName}`
+              : `styles['${className}']`
 
           addDynamicAttributeToJSXTag(root, classAttributeName, classReferenceIdentifier)
         }
@@ -118,7 +121,7 @@ export const createPlugin: ComponentPluginFactory<CSSModulesConfig> = (config =
      * The name of the file is either in the meta of the component generator
      * or we fallback to the name of the component
      */
-    const cssFileName = (meta && meta.fileName) || camelCaseToDashCase(name)
+    const cssFileName = getStyleFileName(uidl)
     dependencies[styleObjectImportName] = {
       type: 'local',
       path: `./${cssFileName}.${cssFileType}`,
diff --git a/packages/teleport-plugin-stencil-css/src/index.ts b/packages/teleport-plugin-stencil-css/src/index.ts
index c3355d18..1d0cea0c 100644
--- a/packages/teleport-plugin-stencil-css/src/index.ts
+++ b/packages/teleport-plugin-stencil-css/src/index.ts
@@ -5,6 +5,7 @@ import {
   cleanupNestedStyles,
   traverseElements,
   transformDynamicStyles,
+  getStyleFileName,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 import {
   createCSSClass,
@@ -75,7 +76,7 @@ export const createPlugin: ComponentPluginFactory<StencilStyleChunkConfig> = (co
     })
 
     if (jssStylesArray.length > 0) {
-      const cssFileName = (uidl.meta && uidl.meta.fileName) || camelCaseToDashCase(uidl.name)
+      const cssFileName = getStyleFileName(uidl)
 
       const decoratorAST = stencilDecorator.content
       const decoratorParam = decoratorAST.expression.arguments[0]
diff --git a/packages/teleport-plugin-vue-app-routing/src/index.ts b/packages/teleport-plugin-vue-app-routing/src/index.ts
index 9172aaa1..32fdfc15 100644
--- a/packages/teleport-plugin-vue-app-routing/src/index.ts
+++ b/packages/teleport-plugin-vue-app-routing/src/index.ts
@@ -34,11 +34,26 @@ export const createPlugin: ComponentPluginFactory<VueRouterConfig> = (config) =>
     const routeDefinitions = uidl.stateDefinitions.route
     const pageDependencyPrefix = options.localDependenciesPrefix || './'
 
+    /* If pages are exported in their own folder and in custom file names.
+         Import statements must then be:
+
+         import Home from '../pages/home/component'
+
+         so the `/component` suffix is computed below.
+      */
+    const pageStrategyOptions = (options.strategy && options.strategy.pages.options) || {}
+    const pageComponentSuffix = pageStrategyOptions.createFolderForEachComponent
+      ? '/' + (pageStrategyOptions.customComponentFileName || 'index')
+      : ''
+
     const routesAST = routes.map((routeNode) => {
       const pageKey = routeNode.content.value.toString()
       const { fileName, componentName, path } = extractPageMetadata(routeDefinitions, pageKey)
 
-      dependencies[componentName] = { type: 'local', path: `${pageDependencyPrefix}${fileName}` }
+      dependencies[componentName] = {
+        type: 'local',
+        path: `${pageDependencyPrefix}${fileName}${pageComponentSuffix}`,
+      }
 
       return t.objectExpression([
         t.objectProperty(t.identifier('name'), t.stringLiteral(pageKey)),
diff --git a/packages/teleport-project-generator-next/src/index.ts b/packages/teleport-project-generator-next/src/index.ts
index 4362ecf4..689e679b 100644
--- a/packages/teleport-project-generator-next/src/index.ts
+++ b/packages/teleport-project-generator-next/src/index.ts
@@ -24,7 +24,7 @@ export const createNextProjectGenerator = () => {
     pages: {
       generator: reactComponentGenerator,
       path: ['pages'],
-      metaDataOptions: {
+      options: {
         usePathAsFileName: true,
         convertDefaultToIndex: true,
       },
diff --git a/packages/teleport-project-generator-next/src/utils.ts b/packages/teleport-project-generator-next/src/utils.ts
index cd951ff4..f6993d67 100644
--- a/packages/teleport-project-generator-next/src/utils.ts
+++ b/packages/teleport-project-generator-next/src/utils.ts
@@ -9,8 +9,7 @@ import * as types from '@babel/types'
 
 import { prefixPlaygroundAssetsURL } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 import { FILE_TYPE, CHUNK_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
-import { EntryFileOptions } from '@teleporthq/teleport-project-generator/dist/cjs/types'
-import { ProjectUIDL, ChunkDefinition } from '@teleporthq/teleport-types'
+import { ProjectUIDL, ChunkDefinition, EntryFileOptions } from '@teleporthq/teleport-types'
 
 export const createDocumentFileChunks = (uidl: ProjectUIDL, options: EntryFileOptions) => {
   const { settings, meta, assets, manifest } = uidl.globals
diff --git a/packages/teleport-project-generator-nuxt/src/index.ts b/packages/teleport-project-generator-nuxt/src/index.ts
index dfde3b68..d71c218c 100644
--- a/packages/teleport-project-generator-nuxt/src/index.ts
+++ b/packages/teleport-project-generator-nuxt/src/index.ts
@@ -22,7 +22,7 @@ export const createNuxtProjectGenerator = () => {
     pages: {
       generator: vueComponentGenerator,
       path: ['pages'],
-      metaDataOptions: {
+      options: {
         usePathAsFileName: true,
         convertDefaultToIndex: true,
       },
diff --git a/packages/teleport-project-generator-preact/src/index.ts b/packages/teleport-project-generator-preact/src/index.ts
index 9c1417c7..d49306e3 100644
--- a/packages/teleport-project-generator-preact/src/index.ts
+++ b/packages/teleport-project-generator-preact/src/index.ts
@@ -2,7 +2,7 @@ import { createProjectGenerator } from '@teleporthq/teleport-project-generator'
 import { createPreactComponentGenerator } from '@teleporthq/teleport-component-generator-preact'
 import { createComponentGenerator } from '@teleporthq/teleport-component-generator'
 
-import reactAppRoutingPlugin from '@teleporthq/teleport-plugin-react-app-routing'
+import { createPlugin as createRouterPlugin } from '@teleporthq/teleport-plugin-react-app-routing'
 import importStatementsPlugin from '@teleporthq/teleport-plugin-import-statements'
 import prettierJS from '@teleporthq/teleport-postprocessor-prettier-js'
 import prettierHTML from '@teleporthq/teleport-postprocessor-prettier-html'
@@ -15,8 +15,9 @@ export const createPreactProjectGenerator = () => {
   const preactComponentGenerator = createPreactComponentGenerator()
   preactComponentGenerator.addMapping(preactProjectMapping as Mapping)
 
+  const routerPlugin = createRouterPlugin({ flavor: 'preact' })
   const routingComponentGenerator = createComponentGenerator()
-  routingComponentGenerator.addPlugin(reactAppRoutingPlugin)
+  routingComponentGenerator.addPlugin(routerPlugin)
   routingComponentGenerator.addPlugin(importStatementsPlugin)
   routingComponentGenerator.addPostProcessor(prettierJS)
 
@@ -31,7 +32,7 @@ export const createPreactProjectGenerator = () => {
     pages: {
       generator: preactComponentGenerator,
       path: ['src', 'routes'],
-      metaDataOptions: {
+      options: {
         createFolderForEachComponent: true,
       },
     },
@@ -39,10 +40,6 @@ export const createPreactProjectGenerator = () => {
       generator: routingComponentGenerator,
       path: ['src', 'components'],
       fileName: 'app',
-      metaDataOptions: {
-        flavour: 'preact',
-        createFolderForEachComponent: true,
-      },
     },
     entry: {
       generator: htmlFileGenerator,
diff --git a/packages/teleport-project-generator/src/file-handlers.ts b/packages/teleport-project-generator/src/file-handlers.ts
index 986b4781..a2b9a236 100644
--- a/packages/teleport-project-generator/src/file-handlers.ts
+++ b/packages/teleport-project-generator/src/file-handlers.ts
@@ -4,10 +4,7 @@ import {
   addTextNode,
   addBooleanAttributeToNode,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/html-utils'
-import {
-  prefixPlaygroundAssetsURL,
-  extractPageMetadata,
-} from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
+import { prefixPlaygroundAssetsURL } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 import { slugify } from '@teleporthq/teleport-shared/dist/cjs/utils/string-utils'
 import { createHTMLNode } from '@teleporthq/teleport-shared/dist/cjs/builders/html-builders'
 import { FILE_TYPE, CHUNK_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
@@ -20,37 +17,19 @@ import {
   ChunkDefinition,
   ComponentUIDL,
   GeneratorOptions,
-  UIDLConditionalNode,
+  ProjectStrategy,
+  EntryFileOptions,
 } from '@teleporthq/teleport-types'
 
 import { DEFAULT_PACKAGE_JSON, DEFAULT_ROUTER_FILE_NAME } from './constants'
-import { EntryFileOptions, PackageJSON, ProjectStrategy } from './types'
+import { PackageJSON } from './types'
 import { generateLocalDependenciesPrefix } from './utils'
 
 export const createPage = async (
-  routeNode: UIDLConditionalNode,
+  pageUIDL: ComponentUIDL,
   strategy: ProjectStrategy,
   options: GeneratorOptions
 ) => {
-  const { value, node } = routeNode.content
-  const pageName = value.toString()
-  const { createFolderForEachComponent } = strategy.pages.metaDataOptions || {
-    createFolderForEachComponent: false,
-  }
-  const { componentName, fileName } = extractPageMetadata(
-    options.projectRouteDefinition,
-    pageName,
-    strategy.pages.metaDataOptions
-  )
-
-  const pageUIDL = {
-    name: componentName,
-    node,
-    meta: {
-      fileName: createFolderForEachComponent ? 'index' : fileName,
-    },
-  }
-
   return strategy.pages.generator.generateComponent(pageUIDL, options)
 }
 
@@ -68,9 +47,10 @@ export const createRouterFile = async (root: ComponentUIDL, strategy: ProjectStr
     routerFilePath,
     strategy.pages.path
   )
+
   const options = {
     localDependenciesPrefix: routerLocalDependenciesPrefix,
-    meta: strategy.router.metaDataOptions,
+    strategy,
   }
 
   root.meta = root.meta || {}
diff --git a/packages/teleport-project-generator/src/index.ts b/packages/teleport-project-generator/src/index.ts
index 3929484d..cc7a9242 100644
--- a/packages/teleport-project-generator/src/index.ts
+++ b/packages/teleport-project-generator/src/index.ts
@@ -1,4 +1,9 @@
-import { injectFilesToPath, resolveLocalDependencies, computePath } from './utils'
+import {
+  injectFilesToPath,
+  resolveLocalDependencies,
+  createPageUIDLs,
+  prepareComponentFilenamesAndPath,
+} from './utils'
 
 import {
   createManifestJSONFile,
@@ -9,12 +14,9 @@ import {
   createEntryFile,
 } from './file-handlers'
 
-import { ProjectStrategy } from './types'
-
 import { DEFAULT_TEMPLATE } from './constants'
 
 import {
-  extractRoutes,
   cloneObject,
   getComponentPath,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
@@ -26,6 +28,7 @@ import {
   GeneratedFolder,
   Mapping,
   ProjectGenerator,
+  ProjectStrategy,
 } from '@teleporthq/teleport-types'
 
 export const createProjectGenerator = (strategy: ProjectStrategy): ProjectGenerator => {
@@ -49,18 +52,26 @@ export const createProjectGenerator = (strategy: ProjectStrategy): ProjectGenera
       throw new Error(schemaValidationResult.errorMsg)
     }
 
-    const parsedInput = Parser.parseProjectJSON(input)
-    const contentValidationResult = validator.validateProjectContent(parsedInput)
+    const uidl = Parser.parseProjectJSON(input)
+    const contentValidationResult = validator.validateProjectContent(uidl)
     if (!contentValidationResult.valid) {
       throw new Error(contentValidationResult.errorMsg)
     }
 
-    // Local dependency paths are set inside the UIDL
-    const uidl = resolveLocalDependencies(parsedInput, strategy)
+    const { components = {}, root } = uidl
+
+    // Based on the routing roles, separate pages into distict UIDLs with their own file names and paths
+    const pageUIDLs = createPageUIDLs(root, strategy)
+
+    // Set the filename and path for each component based on the strategy
+    prepareComponentFilenamesAndPath(components, strategy)
+
+    // Set the local dependency paths based on the relative paths between files
+    resolveLocalDependencies(pageUIDLs, components, strategy)
 
     // Initialize output folder and other reusable structures
-    const { components = {}, root } = uidl
-    const routeNodes = extractRoutes(root)
+    const rootFolder = cloneObject(template || DEFAULT_TEMPLATE)
+    let collectedDependencies: Record<string, string> = {}
     const options: GeneratorOptions = {
       assetsPrefix,
       projectRouteDefinition: root.stateDefinitions.route,
@@ -68,29 +79,27 @@ export const createProjectGenerator = (strategy: ProjectStrategy): ProjectGenera
       skipValidation: true,
     }
 
-    const rootFolder = cloneObject(template || DEFAULT_TEMPLATE)
-    let collectedDependencies: Record<string, string> = {}
+    // Handling pages
+    for (const pageUIDL of pageUIDLs) {
+      const { files, dependencies } = await createPage(pageUIDL, strategy, options)
 
-    // Handling pages, based on the conditionals in the root node
-    for (const routeNode of routeNodes) {
-      const { files, dependencies } = await createPage(routeNode, strategy, options)
-      const fileName: string = routeNode.content.value as string
-      const path = computePath(strategy, fileName)
+      // Pages might be generated inside subfolders in the main pages folder
+      const relativePath = getComponentPath(pageUIDL)
+      const path = strategy.pages.path.concat(relativePath)
 
       injectFilesToPath(rootFolder, path, files)
       collectedDependencies = { ...collectedDependencies, ...dependencies }
     }
 
-    // Handle the components from the UIDL
+    // Handling components
     for (const componentName of Object.keys(components)) {
       const componentUIDL = components[componentName]
+      const { files, dependencies } = await createComponent(componentUIDL, strategy, options)
 
       // Components might be generated inside subfolders in the main components folder
       const relativePath = getComponentPath(componentUIDL)
       const path = strategy.components.path.concat(relativePath)
 
-      const { files, dependencies } = await createComponent(componentUIDL, strategy, options)
-
       injectFilesToPath(rootFolder, path, files)
       collectedDependencies = { ...collectedDependencies, ...dependencies }
     }
diff --git a/packages/teleport-project-generator/src/types.ts b/packages/teleport-project-generator/src/types.ts
index 5c340e1d..bff5d656 100644
--- a/packages/teleport-project-generator/src/types.ts
+++ b/packages/teleport-project-generator/src/types.ts
@@ -1,49 +1,3 @@
-import { ProjectUIDL, ComponentGenerator, ChunkDefinition } from '@teleporthq/teleport-types'
-
-export interface ProjectStrategy {
-  components: {
-    generator: ComponentGenerator
-    path: string[]
-  }
-  pages: {
-    generator: ComponentGenerator
-    path: string[]
-    metaDataOptions?: {
-      usePathAsFileName?: boolean
-      convertDefaultToIndex?: boolean
-      createFolderForEachComponent?: boolean
-    }
-  }
-  router?: {
-    generator: ComponentGenerator
-    path: string[]
-    fileName?: string
-    metaDataOptions?: {
-      flavour?: string
-      createFolderForEachComponent?: boolean
-    }
-  }
-  entry: {
-    generator: ComponentGenerator
-    path: string[]
-    fileName?: string
-    chunkGenerationFunction?: (
-      uidl: ProjectUIDL,
-      options: EntryFileOptions
-    ) => Record<string, ChunkDefinition[]>
-    appRootOverride?: string
-  }
-  static: {
-    prefix?: string
-    path: string[]
-  }
-}
-
-export interface EntryFileOptions {
-  assetsPrefix?: string
-  appRootOverride?: string
-}
-
 export interface PackageJSON {
   name: string
   description: string
diff --git a/packages/teleport-project-generator/src/utils.ts b/packages/teleport-project-generator/src/utils.ts
index ada8cea1..47a1f19f 100644
--- a/packages/teleport-project-generator/src/utils.ts
+++ b/packages/teleport-project-generator/src/utils.ts
@@ -1,33 +1,117 @@
 import {
-  cloneObject,
   traverseElements,
   getComponentFileName,
   getComponentPath,
+  extractRoutes,
+  extractPageMetadata,
 } from '@teleporthq/teleport-shared/dist/cjs/utils/uidl-utils'
 
 import {
   GeneratedFile,
   GeneratedFolder,
-  ProjectUIDL,
   UIDLElement,
   ComponentUIDL,
+  UIDLConditionalNode,
+  UIDLStateDefinition,
+  ProjectStrategy,
 } from '@teleporthq/teleport-types'
 
-import { ProjectStrategy } from './types'
+export const createPageUIDLs = (
+  root: ComponentUIDL,
+  strategy: ProjectStrategy
+): ComponentUIDL[] => {
+  const routeNodes = extractRoutes(root)
+  return routeNodes.map((routeNode) =>
+    createPageUIDL(routeNode, root.stateDefinitions.route, strategy)
+  )
+}
+
+const createPageUIDL = (
+  routeNode: UIDLConditionalNode,
+  routeDefintion: UIDLStateDefinition,
+  strategy: ProjectStrategy
+): ComponentUIDL => {
+  const { value, node } = routeNode.content
+  const pageName = value.toString()
+  const pagesStrategyOptions = strategy.pages.options || {}
+
+  const { componentName, fileName } = extractPageMetadata(
+    routeDefintion,
+    pageName,
+    strategy.pages.options
+  )
 
-export const resolveLocalDependencies = (input: ProjectUIDL, strategy: ProjectStrategy) => {
-  const result = cloneObject(input)
+  // If the file name will not be used as the path (eg: next, nuxt)
+  // And if the option to create each page in its folder is passed (eg: preact)
+  const createPathInOwnFile =
+    !pagesStrategyOptions.usePathAsFileName && pagesStrategyOptions.createFolderForEachComponent
+
+  const meta = createPathInOwnFile
+    ? {
+        fileName: pagesStrategyOptions.customComponentFileName || 'index',
+        styleFileName: pagesStrategyOptions.customStyleFileName || 'style',
+        templateFileName: pagesStrategyOptions.customTemplateFileName || 'template',
+        path: [fileName],
+      }
+    : {
+        fileName,
+        styleFileName: fileName,
+        templateFileName: fileName,
+        path: [],
+      }
 
-  const { components, root } = result
+  return {
+    name: componentName,
+    node,
+    meta,
+  }
+}
 
-  // we don't care about the name of the specific page here, we only want an additional name in the path
-  // so that the import statement is computed correctly (eg: ../../components/ instead of ../components)
-  const pagesPath = computePath(strategy, 'any-page')
-  traverseElements(root.node, (elementNode) => {
-    if (emptyLocalDependency(elementNode)) {
-      setLocalDependencyPath(elementNode, components, pagesPath, strategy.components.path)
+export const prepareComponentFilenamesAndPath = (
+  components: Record<string, ComponentUIDL>,
+  strategy: ProjectStrategy
+) => {
+  const componentStrategyOptions = strategy.components.options || {}
+
+  Object.keys(components).forEach((componentKey) => {
+    const component = components[componentKey]
+    const fileName = getComponentFileName(component)
+    const path = getComponentPath(component)
+
+    // If the component has its own folder, name is 'index' or an override from the strategy.
+    // In this case, the file name (dash converted) is used as the folder name
+    if (componentStrategyOptions.createFolderForEachComponent) {
+      component.meta = {
+        fileName: componentStrategyOptions.customComponentFileName || 'index',
+        styleFileName: componentStrategyOptions.customStyleFileName || 'style',
+        templateFileName: componentStrategyOptions.customTemplateFileName || 'template',
+        path: [...path, fileName],
+      }
+    } else {
+      component.meta = {
+        fileName,
+        styleFileName: fileName,
+        templateFileName: fileName,
+        path,
+      }
     }
   })
+}
+
+export const resolveLocalDependencies = (
+  pageUIDLs: ComponentUIDL[],
+  components: Record<string, ComponentUIDL>,
+  strategy: ProjectStrategy
+) => {
+  pageUIDLs.forEach((pageUIDL) => {
+    const pagePath = getComponentPath(pageUIDL)
+    const fromPath = strategy.pages.path.concat(pagePath)
+    traverseElements(pageUIDL.node, (elementNode) => {
+      if (isLocalDependency(elementNode)) {
+        setLocalDependencyPath(elementNode, components, fromPath, strategy.components.path)
+      }
+    })
+  })
 
   Object.keys(components).forEach((componentKey) => {
     const component = components[componentKey]
@@ -35,17 +119,15 @@ export const resolveLocalDependencies = (input: ProjectUIDL, strategy: ProjectSt
     const fromPath = strategy.components.path.concat(componentPath)
 
     traverseElements(component.node, (elementNode) => {
-      if (emptyLocalDependency(elementNode)) {
+      if (isLocalDependency(elementNode)) {
         setLocalDependencyPath(elementNode, components, fromPath, strategy.components.path)
       }
     })
   })
-
-  return result
 }
 
-const emptyLocalDependency = (elementNode: UIDLElement) =>
-  elementNode.dependency && elementNode.dependency.type === 'local' && !elementNode.dependency.path
+const isLocalDependency = (elementNode: UIDLElement) =>
+  elementNode.dependency && elementNode.dependency.type === 'local'
 
 const setLocalDependencyPath = (
   elementNode: UIDLElement,
@@ -197,12 +279,3 @@ const findSubFolderByName = (rootFolder: GeneratedFolder, folderName: string): G
 const findFileInFolder = (file: GeneratedFile, folder: GeneratedFolder) => {
   return folder.files.find((f) => f.name === file.name && f.fileType === file.fileType)
 }
-
-export const computePath = (strategy: ProjectStrategy, fileName: string) => {
-  const { createFolderForEachComponent } = strategy.pages.metaDataOptions || {
-    createFolderForEachComponent: false,
-  }
-  const { path } = strategy.pages
-
-  return createFolderForEachComponent ? [...path, fileName] : path
-}
diff --git a/packages/teleport-shared/src/utils/uidl-utils.ts b/packages/teleport-shared/src/utils/uidl-utils.ts
index cdba9ae8..d4dfb447 100644
--- a/packages/teleport-shared/src/utils/uidl-utils.ts
+++ b/packages/teleport-shared/src/utils/uidl-utils.ts
@@ -73,6 +73,24 @@ export const getComponentFileName = (component: ComponentUIDL) => {
   return camelCaseToDashCase(name)
 }
 
+export const getStyleFileName = (component: ComponentUIDL) => {
+  const componentFileName = getComponentFileName(component)
+
+  // If component meta style file name is not set, we default to the component file name
+  return component.meta && component.meta.styleFileName
+    ? component.meta.styleFileName
+    : componentFileName
+}
+
+export const getTemplateFileName = (component: ComponentUIDL) => {
+  const componentFileName = getComponentFileName(component)
+
+  // If component meta style file name is not set, we default to the component file name
+  return component.meta && component.meta.templateFileName
+    ? component.meta.templateFileName
+    : componentFileName
+}
+
 export const getComponentPath = (component: ComponentUIDL) =>
   component.meta ? component.meta.path : []
 
diff --git a/packages/teleport-types/src/generators.ts b/packages/teleport-types/src/generators.ts
index 6d456969..4d898174 100644
--- a/packages/teleport-types/src/generators.ts
+++ b/packages/teleport-types/src/generators.ts
@@ -69,6 +69,7 @@ export interface GeneratorOptions {
   mapping?: Mapping
   skipValidation?: boolean
   projectRouteDefinition?: UIDLStateDefinition
+  strategy?: ProjectStrategy
 }
 
 export type CodeGeneratorFunction<T> = (content: T) => string
@@ -103,6 +104,55 @@ export interface ProjectGenerator {
   getAssetsPath: () => string
 }
 
+export interface ProjectStrategy {
+  components: {
+    generator: ComponentGenerator
+    path: string[]
+    options?: {
+      createFolderForEachComponent?: boolean
+      customComponentFileName?: string // only used when createFolderForEachComponent is true
+      customStyleFileName?: string
+      customTemplateFileName?: string
+    }
+  }
+  pages: {
+    generator: ComponentGenerator
+    path: string[]
+    options?: {
+      usePathAsFileName?: boolean
+      convertDefaultToIndex?: boolean
+      createFolderForEachComponent?: boolean
+      customComponentFileName?: string // only used when createFolderForEachComponent is true
+      customStyleFileName?: string
+      customTemplateFileName?: string
+    }
+  }
+  router?: {
+    generator: ComponentGenerator
+    path: string[]
+    fileName?: string
+  }
+  entry: {
+    generator: ComponentGenerator
+    path: string[]
+    fileName?: string
+    chunkGenerationFunction?: (
+      uidl: ProjectUIDL,
+      options: EntryFileOptions
+    ) => Record<string, ChunkDefinition[]>
+    appRootOverride?: string
+  }
+  static: {
+    prefix?: string
+    path: string[]
+  }
+}
+
+export interface EntryFileOptions {
+  assetsPrefix?: string
+  appRootOverride?: string
+}
+
 export interface GeneratedFolder {
   name: string
   files: GeneratedFile[]
diff --git a/packages/teleport-types/src/uidl.ts b/packages/teleport-types/src/uidl.ts
index 8d73d0ef..6030c7e1 100755
--- a/packages/teleport-types/src/uidl.ts
+++ b/packages/teleport-types/src/uidl.ts
@@ -34,6 +34,8 @@ export interface ComponentUIDL {
   node: UIDLNode
   meta?: {
     fileName?: string
+    styleFileName?: string
+    templateFileName?: string
     path?: string[]
   }
   propDefinitions?: Record<string, UIDLPropDefinition>


Test patch:
diff --git a/packages/teleport-plugin-react-css-modules/__tests__/index.ts b/packages/teleport-plugin-react-css-modules/__tests__/index.ts
index 08eccc09..63d60bc6 100644
--- a/packages/teleport-plugin-react-css-modules/__tests__/index.ts
+++ b/packages/teleport-plugin-react-css-modules/__tests__/index.ts
@@ -1,43 +1,23 @@
+import * as types from '@babel/types'
 import {
   component,
   elementNode,
+  dynamicNode,
   staticNode,
 } from '@teleporthq/teleport-shared/dist/cjs/builders/uidl-builders'
-import { ComponentStructure, ChunkDefinition } from '@teleporthq/teleport-types'
+import { ComponentStructure } from '@teleporthq/teleport-types'
 import { createPlugin } from '../src/index'
-import { CHUNK_TYPE, FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+import { createComponentChunk, setupPluginStructure } from './mocks'
+import { FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
 
 describe('plugin-react-css-modules', () => {
-  const plugin = createPlugin()
-  const componentChunk: ChunkDefinition = {
-    name: 'jsx-component',
-    meta: {
-      nodesLookup: {
-        container: {
-          openingElement: {
-            name: {
-              name: '',
-            },
-            attributes: [],
-          },
-        },
-      },
-      dynamicRefPrefix: {
-        prop: 'props.',
-      },
-    },
-    type: CHUNK_TYPE.AST,
-    fileType: FILE_TYPE.JS,
-    linkAfter: ['import-local'],
-    content: {},
-  }
-
   it('generates no chunk if no styles exist', async () => {
+    const plugin = createPlugin()
     const uidlSample = component('CSSModules', elementNode('container'))
     const structure: ComponentStructure = {
       uidl: uidlSample,
       options: {},
-      chunks: [componentChunk],
+      chunks: [createComponentChunk()],
       dependencies: {},
     }
 
@@ -47,31 +27,138 @@ describe('plugin-react-css-modules', () => {
   })
 
   it('generates a string chunk out of the styles and adds the className', async () => {
-    const style = {
-      height: staticNode('100px'),
-    }
-    const element = elementNode('container', {}, [], { type: 'package' }, style)
-    element.content.key = 'container'
-    const uidlSample = component('CSSModules', element)
+    const plugin = createPlugin()
+    const structure = setupPluginStructure()
+    const { chunks } = await plugin(structure)
 
-    const structure: ComponentStructure = {
-      uidl: uidlSample,
-      options: {},
-      chunks: [componentChunk],
-      dependencies: {},
-    }
+    expect(chunks.length).toBe(2)
+    expect(chunks[1].type).toBe('string')
+    expect(chunks[1].fileType).toBe(FILE_TYPE.CSS)
+    expect(chunks[1].content).toContain('height: 100px;')
 
+    const nodeReference = chunks[0].meta.nodesLookup.container
+    expect(nodeReference.openingElement.attributes.length).toBe(1)
+
+    const classNameAttr = nodeReference.openingElement.attributes[0]
+    expect(classNameAttr.name.name).toBe('className')
+    expect(classNameAttr.value.expression.name).toBe('styles.container')
+  })
+
+  it('generates a string chunk out of the styles and adds the className between brackets', async () => {
+    const plugin = createPlugin()
+    const structure = setupPluginStructure('list-container')
     const { chunks } = await plugin(structure)
 
     expect(chunks.length).toBe(2)
     expect(chunks[1].type).toBe('string')
+    expect(chunks[1].fileType).toBe(FILE_TYPE.CSS)
     expect(chunks[1].content).toContain('height: 100px;')
 
-    const nodeReference = componentChunk.meta.nodesLookup.container
+    const nodeReference = chunks[0].meta.nodesLookup['list-container']
     expect(nodeReference.openingElement.attributes.length).toBe(1)
 
     const classNameAttr = nodeReference.openingElement.attributes[0]
     expect(classNameAttr.name.name).toBe('className')
-    expect(classNameAttr.value.expression.name).toBe("styles['container']")
+    expect(classNameAttr.value.expression.name).toBe("styles['list-container']")
+  })
+
+  it('generates a string chunk out of the styles and adds the className in camel case', async () => {
+    const plugin = createPlugin({ camelCaseClassNames: true })
+    const structure = setupPluginStructure('list-container')
+    const { chunks } = await plugin(structure)
+
+    expect(chunks.length).toBe(2)
+    expect(chunks[1].type).toBe('string')
+    expect(chunks[1].fileType).toBe(FILE_TYPE.CSS)
+    expect(chunks[1].content).toContain('height: 100px;')
+
+    const nodeReference = chunks[0].meta.nodesLookup['list-container']
+    expect(nodeReference.openingElement.attributes.length).toBe(1)
+
+    const classNameAttr = nodeReference.openingElement.attributes[0]
+    expect(classNameAttr.name.name).toBe('className')
+    expect(classNameAttr.value.expression.name).toBe('styles.listContainer')
+  })
+
+  it('generates a string chunk out of the styles and adds the class attribute', async () => {
+    const plugin = createPlugin({ classAttributeName: 'class' })
+    const structure = setupPluginStructure('list-container')
+    const { chunks } = await plugin(structure)
+
+    expect(chunks.length).toBe(2)
+    expect(chunks[1].type).toBe('string')
+    expect(chunks[1].fileType).toBe(FILE_TYPE.CSS)
+    expect(chunks[1].content).toContain('height: 100px;')
+
+    const nodeReference = chunks[0].meta.nodesLookup['list-container']
+    expect(nodeReference.openingElement.attributes.length).toBe(1)
+
+    const classNameAttr = nodeReference.openingElement.attributes[0]
+    expect(classNameAttr.name.name).toBe('class')
+    expect(classNameAttr.value.expression.name).toBe("styles['list-container']")
+  })
+
+  it('generates a string chunk of type CSSMODULE', async () => {
+    const plugin = createPlugin({ moduleExtension: true })
+    const structure = setupPluginStructure('list-container')
+    const { chunks } = await plugin(structure)
+
+    expect(chunks.length).toBe(2)
+    expect(chunks[1].type).toBe('string')
+    expect(chunks[1].fileType).toBe(FILE_TYPE.CSSMODULE)
+    expect(chunks[1].content).toContain('height: 100px;')
+  })
+
+  it('inlines dynamic style and does not generate a new chunk if no static styles are present', async () => {
+    const plugin = createPlugin()
+    const structure = setupPluginStructure('container', {
+      height: dynamicNode('prop', 'height'),
+    })
+
+    const { chunks } = await plugin(structure)
+    expect(chunks.length).toBe(1)
+
+    const nodeReference = chunks[0].meta.nodesLookup.container
+    expect(nodeReference.openingElement.attributes.length).toBe(1)
+
+    const styleAttr = nodeReference.openingElement.attributes[0]
+    expect(styleAttr.name.name).toBe('style')
+
+    const dynamicStyleObject = styleAttr.value.expression as types.ObjectExpression
+    const heightProperty = dynamicStyleObject.properties[0] as types.ObjectProperty
+    expect(heightProperty.key.value).toBe('height')
+    expect((heightProperty.value as types.MemberExpression).object.name).toBe('props.')
+    expect((heightProperty.value as types.MemberExpression).property.name).toBe('height')
+  })
+
+  it('inlines dynamic style and generates a new chunk with the static styles', async () => {
+    const plugin = createPlugin()
+    const structure = setupPluginStructure('container', {
+      height: dynamicNode('prop', 'height'),
+      width: staticNode('auto'),
+    })
+
+    const { chunks } = await plugin(structure)
+    expect(chunks.length).toBe(2)
+
+    expect(chunks[1].type).toBe('string')
+    expect(chunks[1].fileType).toBe(FILE_TYPE.CSS)
+    expect(chunks[1].content).toContain('width: auto;')
+
+    const nodeReference = chunks[0].meta.nodesLookup.container
+    expect(nodeReference.openingElement.attributes.length).toBe(2)
+
+    const classNameAttr = nodeReference.openingElement.attributes[0]
+    expect(classNameAttr.name.name).toBe('className')
+    expect(classNameAttr.value.expression.name).toBe('styles.container')
+
+    const styleAttr = nodeReference.openingElement.attributes[1]
+    expect(styleAttr.name.name).toBe('style')
+
+    const dynamicStyleObject = styleAttr.value.expression as types.ObjectExpression
+    const heightProperty = dynamicStyleObject.properties[0] as types.ObjectProperty
+    expect(heightProperty.key.value).toBe('height')
+    expect((heightProperty.value as types.MemberExpression).object.name).toBe('props.')
+    expect((heightProperty.value as types.MemberExpression).property.name).toBe('height')
   })
 })
diff --git a/packages/teleport-plugin-react-css-modules/__tests__/mocks.ts b/packages/teleport-plugin-react-css-modules/__tests__/mocks.ts
new file mode 100644
index 00000000..52392b8c
--- /dev/null
+++ b/packages/teleport-plugin-react-css-modules/__tests__/mocks.ts
@@ -0,0 +1,59 @@
+import {
+  ChunkDefinition,
+  ComponentStructure,
+  UIDLStyleDefinitions,
+} from '@teleporthq/teleport-types'
+import { CHUNK_TYPE, FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
+import {
+  component,
+  elementNode,
+  staticNode,
+} from '@teleporthq/teleport-shared/dist/cjs/builders/uidl-builders'
+
+export const createComponentChunk = (elementKey: string = 'container') => {
+  const componentChunk: ChunkDefinition = {
+    name: 'jsx-component',
+    meta: {
+      nodesLookup: {
+        [elementKey]: {
+          openingElement: {
+            name: {
+              name: '',
+            },
+            attributes: [],
+          },
+        },
+      },
+      dynamicRefPrefix: {
+        prop: 'props.',
+      },
+    },
+    type: CHUNK_TYPE.AST,
+    fileType: FILE_TYPE.JS,
+    linkAfter: ['import-local'],
+    content: {},
+  }
+
+  return componentChunk
+}
+
+export const setupPluginStructure = (
+  elementKey: string = 'container',
+  styleDefinition: UIDLStyleDefinitions = null
+) => {
+  const style = styleDefinition || {
+    height: staticNode('100px'),
+  }
+  const element = elementNode('container', {}, [], null, style)
+  element.content.key = elementKey
+  const uidlSample = component('CSSModules', element)
+
+  const structure: ComponentStructure = {
+    uidl: uidlSample,
+    options: {},
+    chunks: [createComponentChunk(elementKey)],
+    dependencies: {},
+  }
+
+  return structure
+}
diff --git a/packages/teleport-project-generator/__tests__/file-handlers.ts b/packages/teleport-project-generator/__tests__/file-handlers.ts
index 41f05f97..b1ff9996 100644
--- a/packages/teleport-project-generator/__tests__/file-handlers.ts
+++ b/packages/teleport-project-generator/__tests__/file-handlers.ts
@@ -1,6 +1,6 @@
 import { handlePackageJSON, createEntryFile, createManifestJSONFile } from '../src/file-handlers'
 import { PackageJSON } from '../src/types'
-import { GeneratedFolder, ProjectUIDL, HastNode, HastText } from '@teleporthq/teleport-types'
+import { GeneratedFolder, ProjectUIDL } from '@teleporthq/teleport-types'
 import { component, elementNode } from '@teleporthq/teleport-shared/dist/cjs/builders/uidl-builders'
 import { FILE_TYPE } from '@teleporthq/teleport-shared/dist/cjs/constants'
 
@@ -25,7 +25,7 @@ describe('createHtmlIndexFile', () => {
 describe('createManifestJSONFile', () => {
   it('returns manifest file with prefixed assets', () => {
     const assetsPrefix = 'playground'
-    const result = createManifestJSONFile(uidlSample, assetsPrefix)
+    const result = createManifestJSONFile(uidlSample as ProjectUIDL, assetsPrefix)
 
     expect(result.name).toBe('manifest')
     expect(result.fileType).toBe('json')
@@ -33,7 +33,7 @@ describe('createManifestJSONFile', () => {
   })
 
   it('returns manifest file with no prefixed assets', () => {
-    const result = createManifestJSONFile(uidlSample)
+    const result = createManifestJSONFile(uidlSample as ProjectUIDL)
 
     expect(result.name).toBe('manifest')
     expect(result.fileType).toBe('json')
diff --git a/packages/teleport-project-generator/__tests__/index.ts b/packages/teleport-project-generator/__tests__/index.ts
index c1f02bd7..daf595b9 100644
--- a/packages/teleport-project-generator/__tests__/index.ts
+++ b/packages/teleport-project-generator/__tests__/index.ts
@@ -1,10 +1,10 @@
+import { ProjectUIDL } from '@teleporthq/teleport-types'
 import { createProjectGenerator } from '../src'
 import { resolveLocalDependencies } from '../src/utils'
 import { mockMapping, firstStrategy, secondStrategy } from './mocks'
 
 // @ts-ignore
 import projectUIDL from '../../../examples/test-samples/project-sample.json'
-import { ProjectUIDL } from '@teleporthq/teleport-types'
 
 describe('Generic Project Generator', () => {
   describe('with the same component generator for pages and components', () => {
@@ -32,15 +32,17 @@ describe('Generic Project Generator', () => {
     it('calls the generators according to the strategy', async () => {
       await generator.generateProject(projectUIDL)
 
+      const uidl = projectUIDL as ProjectUIDL
+
       // This adds the local dependencies on the UIDL, so we can proper assert below
-      const resolvedUIDL = resolveLocalDependencies(projectUIDL as ProjectUIDL, firstStrategy)
+      resolveLocalDependencies([], uidl.components, firstStrategy)
 
       expect(componentsGenerator.generateComponent).toBeCalledTimes(7)
       expect(componentsGenerator.generateComponent).toBeCalledWith(
-        resolvedUIDL.components.ExpandableArea,
+        expect.objectContaining({ name: 'ExpandableArea' }),
         {
           assetsPrefix: '/test/static',
-          projectRouteDefinition: resolvedUIDL.root.stateDefinitions.route,
+          projectRouteDefinition: uidl.root.stateDefinitions.route,
           mapping: {},
           skipValidation: true,
         }
@@ -49,15 +51,18 @@ describe('Generic Project Generator', () => {
       expect(routerGenerator.generateComponent).toBeCalledTimes(1)
 
       const routerUIDL = {
-        ...resolvedUIDL.root,
+        ...uidl.root,
         meta: {
           fileName: 'index',
         },
       }
 
-      expect(routerGenerator.generateComponent).toBeCalledWith(routerUIDL, {
-        localDependenciesPrefix: './pages/',
-      })
+      expect(routerGenerator.generateComponent).toBeCalledWith(
+        routerUIDL,
+        expect.objectContaining({
+          localDependenciesPrefix: './pages/',
+        })
+      )
     })
   })
 
@@ -89,15 +94,17 @@ describe('Generic Project Generator', () => {
     it('calls the generators according to the strategy', async () => {
       await generator.generateProject(projectUIDL)
 
+      const uidl = projectUIDL as ProjectUIDL
+
       // This adds the local dependencies on the UIDL, so we can proper assert below
-      const resolvedUIDL = resolveLocalDependencies(projectUIDL as ProjectUIDL, secondStrategy)
+      resolveLocalDependencies([], uidl.components, secondStrategy)
 
       expect(componentsGenerator.generateComponent).toBeCalledTimes(4)
       expect(componentsGenerator.generateComponent).toBeCalledWith(
-        resolvedUIDL.components.ExpandableArea,
+        expect.objectContaining({ name: 'ExpandableArea' }),
         {
           assetsPrefix: '/static',
-          projectRouteDefinition: resolvedUIDL.root.stateDefinitions.route,
+          projectRouteDefinition: uidl.root.stateDefinitions.route,
           mapping: {},
           skipValidation: true,
         }
@@ -109,7 +116,7 @@ describe('Generic Project Generator', () => {
         }),
         {
           assetsPrefix: '/static',
-          projectRouteDefinition: resolvedUIDL.root.stateDefinitions.route,
+          projectRouteDefinition: uidl.root.stateDefinitions.route,
           mapping: {},
           skipValidation: true,
         }
@@ -117,16 +124,19 @@ describe('Generic Project Generator', () => {
       expect(entryGenerator.linkCodeChunks).toBeCalledTimes(1)
 
       const routerUIDL = {
-        ...resolvedUIDL.root,
+        ...uidl.root,
         meta: {
           fileName: secondStrategy.router.fileName,
         },
       }
 
       expect(routerGenerator.generateComponent).toBeCalledTimes(1)
-      expect(routerGenerator.generateComponent).toBeCalledWith(routerUIDL, {
-        localDependenciesPrefix: './pages/',
-      })
+      expect(routerGenerator.generateComponent).toBeCalledWith(
+        routerUIDL,
+        expect.objectContaining({
+          localDependenciesPrefix: './pages/',
+        })
+      )
     })
   })
 })
diff --git a/packages/teleport-project-generator/__tests__/mocks.ts b/packages/teleport-project-generator/__tests__/mocks.ts
index 4f266a62..6764854a 100644
--- a/packages/teleport-project-generator/__tests__/mocks.ts
+++ b/packages/teleport-project-generator/__tests__/mocks.ts
@@ -1,5 +1,9 @@
-import { Mapping, CompiledComponent, GeneratedFolder } from '@teleporthq/teleport-types'
-import { ProjectStrategy } from '../src/types'
+import {
+  Mapping,
+  CompiledComponent,
+  GeneratedFolder,
+  ProjectStrategy,
+} from '@teleporthq/teleport-types'
 
 export const mockMapping: Mapping = {
   elements: {