{"data":{"site":{"siteMetadata":{"title":"Serge van den Oever's weblog","author":"Serge van den Oever"}},"markdownRemark":{"id":"386ce40a-920a-53a4-8acf-081ba6c4e369","html":"

In this blog post I describe my adventures to convert an “out of the box” Sitecore 9.1 with JSS in React with TypeScript project from scratch. To get started head over to the Quickstart |Sitecore JSS Documentation\nand follow the steps to create a disconnected JSS starter project:

\n
    \n
  1. npm install -g @sitecore-jss/sitecore-jss-cli
  2. \n
  3. jss create hello-jss-typescript react
  4. \n
  5. git init
  6. \n
  7. cd hello-jss-typescript
  8. \n
  9. git init
  10. \n
  11. git add -A
  12. \n
  13. git commit -m "first commit"
  14. \n
  15. git remote add origin https://github.com/macaw-interactive/hello-jss-sitecore.git
  16. \n
  17. git push -u origin master
  18. \n
  19. jss start
  20. \n
\n

We now have the starter website running on http://localhost:3000.

\n

Now we can scaffold a new Hero component using jss scaffold Hero.\nThe scaffolder generates the new component in it’s own folder src/components/Hero named index.js.\nThe easy way to introduce TypeScript is described by my colleague Gary Wenneker in his blog post Sitecore JSS: Get Typed!:

\n\n

The JSS tooling picks up the .js component files in the script "start:watch-components": "node scripts/generate-component-factory.js --watch" for mapping the React components to JSS components.

\n

Problem with this approach is that source tree is cluttered with generated .js files\nnext to the TypeScript files that are difficult to exclude from source control.

\n

Another approach in using TypeScript

\n

In this blog post we describe another approach in using TypeScript in you Sitecore JSS project.\nThe blog post is accompanied by the GitHub repository\nhello-jss-typescript\nwith the required modifications for a simple “Hello world” app as a reference for you\nadventures.

\n

The first step to execute is to install the required TypeScript tooling\nand type definitions:

\n

npm install typescript @types/node @types/react @types/react-dom

\n

Now add a tsconfig.json file in the root tailered for JSS development.\nBecause the JSS tool-set is based on the magnificent tool-set of Create React App we took as a starter point the\ntsconfig.json as generated by create-react-app. We need to make some modifications to tailer it to the needs of the JSS project. Those changes are already\nincluded is the tsconfig.json below. The rationale behind these changes are described below where appropriate.

\n
{\n    \"compilerOptions\": {\n      \"target\": \"es5\",\n      \"allowJs\": false,\n      \"skipLibCheck\": false,\n      \"esModuleInterop\": true,\n      \"allowSyntheticDefaultImports\": true,\n      \"strict\": true,\n      \"forceConsistentCasingInFileNames\": true,\n      \"module\": \"esnext\",\n      \"moduleResolution\": \"node\",\n      \"resolveJsonModule\": true,\n      \"isolatedModules\": true,\n      \"noEmit\": true,\n      \"jsx\": \"preserve\",\n      \"lib\": [\"es2018\", \"dom\"],\n    },\n    \"include\": [\n      \"src\"\n    ],\n    \"types\": [\"node\"]\n  }
\n

Scaffolding a Hero component

\n

We now scaffold the Hero component using the command jss scaffold Hero as\ndescribed in Scaffolding a JSS Component.\nThis will generate two files:

\n\n

The generated src\\components\\Hero\\index.js looks like:

\n
import React from 'react';\nimport { Text } from '@sitecore-jss/sitecore-jss-react';\n\nconst Hero = (props) => (\n  <div>\n    <p>Hero Component</p>\n    <Text field={props.fields.heading} />\n  </div>\n);\n\nexport default Hero;
\n

rename the file to src\\components\\Hero\\index.tsx and rewrite the code to:

\n
import * as React from 'react';\nimport { Text } from '@sitecore-jss/sitecore-jss-react';\n\ntype HeroPropsFields = {\n  heading: {\n    value?: string;\n    editable?: string;\n  } \n}\ntype HeroProps = {\n  fields: HeroPropsFields;\n};\n\ntype HeroAllProps = HeroProps;\n\nconst Hero: React.SFC<HeroAllProps> = (props: HeroAllProps) => (\n  <div>\n    <p>Hero Component</p>\n    <Text field={props.fields.heading} />\n  </div>\n);\n\nexport default Hero;
\n

To include the Hero component in a route layout in disconnected mode open the file data\\routes\\en.yml\nand add the Hero component to the jss-main placeholder:

\n
placeholders:\n  jss-main:\n  - componentName: Hero\n    fields:\n      heading: Serge's hero component!\n  - componentName: ContentBlock\n    fields:
\n

Generating the component factory

\n

The component factory does the mapping op React components to JSS components. It provides\nthe mapping between a string name based on the component folder and name the React component instance.\nWhen the Sitecore Layout service returns a layout definition, it returns the components by name.\nBased on the mapping provided by the component factory the component hierarchy can be\nconstructed for the layout.

\n

The script scripts/generate-component-factory.js is responsible for the generation of\nthis mapping and produces the componentFactory.js output file. We want to use TypeScript for this file because we want the generated import statements also to search for .ts\nand .tsx files. must be changed to the .ts extensions. This means that we need to change\nthe output file to componentFactory.ts. We also want the component factory generation script\nto pick up .ts and .tsx files so components can be written in both JavaScript and TypeScript. This means that we need to add the following lines to the script:

\n
const componentFactoryPath = path.resolve('src/temp/componentFactory.ts');
\n

and

\n
      fs.existsSync(path.join(componentFolderFullPath, 'index.js')) ||\n      fs.existsSync(path.join(componentFolderFullPath, 'index.jsx')) ||\n      fs.existsSync(path.join(componentFolderFullPath, 'index.ts')) ||      fs.existsSync(path.join(componentFolderFullPath, 'index.tsx'))
\n

Rewriting the JSS tool scripts like scripts/generate-component-factory.js to TypeScript\nas well makes no sense because they are node scripts, and we don’t want to introduce a\ncompilation step in order to run them, although ts-node is a viable solution for directly running TypeScript files in a development environment. Another\nreason is that the JSS tool-set calls these scripts.

\n

Compiling the component factory

\n

On compilation of the componentFactory.ts file we got the error:

\n
Type error: Could not find a declaration file for module '../components/Hero'. 'C:/P/hello-jss-typescript/src/components/Hero/index.js' implicitly has an 'any' type.  TS7016
\n

We got similar errors on more files. The easiest way to resolve this is to set the option\n"noImplicitAny": false in the tsconfig.json. This is not a really sustainable option\nbecause using types instead of any is the whole purpose of using TypeScript, and settings this\noption means that also the application specific code is not checked if types are omitted.

\n

Make sure that you don’t have an old src/temp/componentFactory.js file because this file will be picked up in the compilation as well.

\n

We can now run jss start again and get our TypeScript Hero component working in the disconnected site:

\n

\n \n \n \n \n \n \n \n \n \n

\n

Getting connected

\n

Now it’s time to deploy our JSS app to Sitecore as described in\nJSS Server Setup.

\n

Part of these steps as described in App Deployment is:

\n\n

After everything is configured we can run jss setup and answer the questions (you answers may vary):

\n
Is your Sitecore instance on this machine or accessible via network share? [y/n]: y\nPath to the Sitecore folder (e.g. c:\\inetpub\\wwwroot\\my.siteco.re): c:\\inetpub\\wwwroot\\sc910.sc\nSitecore hostname (e.g. http://myapp.local.siteco.re; see /sitecore/config; ensure added to hosts): http://hello-jss-typescript.dev.local\nSitecore import service URL [http://hello-jss-typescript.dev.local/sitecore/api/jss/import]:\nSitecore API Key (ID of API key item): {57231674-4CC9-48AA-AFF0-190DB9D68FE1}\nPlease enter your deployment secret (32+ random chars; or press enter to generate one):\nDeployment secret has been generated. Ensure the JSS app config on the Sitecore end has the same secret set.\nDeploy secret Sitecore config written to C:\\P\\hello-jss-typescript\\sitecore\\config\\hello-jss-typescript.deploysecret.config\nEnsure this configuration is deployed to Sitecore.\nJSS connection settings saved to C:\\P\\hello-jss-typescript\\scjssconfig.json\n\nNEXT STEPS\n* Ensure the hostName in /sitecore/config/*.config is configured as hello-jss-typescript.dev.local, and in hosts file if needed.\n* Deploy your configuration (i.e. 'jss deploy config')\n* Deploy your app (i.e. 'jss deploy app -c -d')\n* Test your app in integrated mode by visiting http://hello-jss-typescript.dev.local
\n

As specified in the NEXT STEPS we should deploy the configuration using jss deploy config\nand deploy the app using jss deploy app -c -d.

\n

Building the server bundle

\n

When we deploy the app we get a compilation error in building the server.bundle.js:

\n
ERROR in ./src/AppRoot.js\nModule not found: Error: Can't resolve './temp/componentFactory' in 'C:\\P\\hello-jss-typescript\\src'\n @ ./src/AppRoot.js 5:0-55 32:22-38\n @ ./server/server.js
\n

Because server/server.js does an import of AppRoot from ../src/AppRoot and\nAppRoot does an import of ./temp/componentFactory which is generated as a\nTypeScript file we have an issue. This can be fixed by renaming server/server.js\nto server/server.tsx and modifying server/server.webpack.config.js to have as\nentry entry: path.resolve(__dirname, './server.tsx'). Because the server bundle specific\nwebpack configuration file does a require of babel-preset-react-app it can use the\nBabel 7 configuration to compile TypeScript files out of the box.

\n

Next step is to rename the file src/AppRoot.js to src/AppRoot.tsx. This is the main\nentry point of the app invoked by the renderer for server and client rendering. It imports\nthe generated src/temp/componentFactory file, but we can’t import a .ts file\nfrom a .js file. It won’t be resolved.

\n

For the Typescript compilation of the server bundle we need the same tsconfig.json configuration\nas for the client bundle, with only the include path changed. For this we can create the file\nserver/tsconfig with the following contents:

\n
{\n  \"extends\": \"../tsconfig.json\",\n  \"include\": [\n    \".\"\n  ]\n}
\n

Including the content of the above described files will make this blog post too large, so please refer\nto the GitHub repository hello-jss-typescript\nfor the details.

\n

If we now the app using jss deploy app -c -d we can navigate to http://hello-jss-typescript.dev.local/ and\nthe site is rendered correctly.

\n

The Sitecore layout service

\n

The layout of the rendered React page for a given route is provided by the\nSitecore layout service.

\n

In our case a hit on http://hello-jss-typescript.dev.local/sitecore/api/layout/render/jss?item=/&sc_apikey={57231674-4CC9-48AA-AFF0-190DB9D68FE1} (your API key will vary) gives us:

\n
{\n  \"sitecore\": {\n    \"context\": {\n      \"pageEditing\": false,\n      \"site\": {\n        \"name\": \"hello-jss-typescript\"\n      },\n      \"pageState\": \"normal\",\n      \"language\": \"en\"\n    },\n    \"route\": {\n      \"name\": \"home\",\n      \"displayName\": \"home\",\n      \"fields\": {\n        \"pageTitle\": {\n          \"value\": \"Welcome to Sitecore JSS\"\n        }\n      },\n      \"databaseName\": \"master\",\n      \"deviceId\": \"fe5d7fdf-89c0-4d99-9aa3-b5fbd009c9f3\",\n      \"itemId\": \"75b2a549-f227-51dd-98dd-599438514aad\",\n      \"itemLanguage\": \"en\",\n      \"itemVersion\": 1,\n      \"layoutId\": \"a0909742-4629-5ae4-a71a-1fe76a57379a\",\n      \"templateId\": \"ab82556d-8208-5edd-a980-be88546ccf5b\",\n      \"templateName\": \"App Route\",\n      \"placeholders\": {\n        \"jss-main\": [\n          {\n            \"uid\": \"3af894c3-37f9-52fa-943b-561cdf38da1a\",\n            \"componentName\": \"Hero\",\n            \"dataSource\": \"{0DC131EC-C855-5F8B-BFB9-62606ABB2DF2}\",\n            \"fields\": {\n              \"heading\": {\n                \"value\": \"Serge's hero component!\"\n              }\n            }\n          },\n          {\n            \"uid\": \"7418112d-b1c0-53fe-9256-b807b16fe3f4\",\n            \"componentName\": \"ContentBlock\",\n            \"dataSource\": \"{20448A26-84CB-52B7-BCE7-8D4851E8AABC}\",\n            \"fields\": {\n              \"heading\": {\n                \"value\": \"Welcome to Sitecore JSS\"\n              },\n              \"content\": {\n                \"value\": \"<p>Thanks for using JSS. Here are some resources to get you started:</p>\\n\\n<h3><a href=\\\"https://jss.sitecore.net\\\" rel=\\\"noopener noreferrer\\\">Documentation</a></h3>\\n<p>The official JSS documentation can help you with any JSS task from getting started to advanced techniques.</p>\\n\\n<h3><a href=\\\"/styleguide\\\">Styleguide</a></h3>\\n<p>The JSS styleguide is a living example of how to use JSS, hosted right in this app.\\nIt demonstrates most of the common patterns that JSS implementations may need to use,\\nas well as useful architectural patterns.</p>\\n\\n<h3><a href=\\\"/graphql\\\">GraphQL</a></h3>\\n<p>JSS features integration with the Sitecore GraphQL API to enable fetching non-route data from Sitecore - or from other internal backends as an API aggregator or proxy.\\nThis route is a living example of how to use an integrate with GraphQL data in a JSS app.</p>\\n\\n<div class=\\\"alert alert-dark\\\">\\n  <h4>This app is a boilerplate</h4>\\n  <p>The JSS samples are a boilerplate, not a library. That means that any code in this app is meant for you to own and customize to your own requirements.</p>\\n  <p>Want to get change the lint settings? Do it. Want to read manifest data from a MongoDB database? Go for it. This app is yours.</p>\\n</div>\\n\\n<div class=\\\"alert alert-dark\\\">\\n  <h4>How to start with an empty app</h4>\\n  <p>To remove all of the default sample content (the Styleguide and GraphQL routes) and start out with an empty JSS app:</p>\\n  <ol>\\n    <li>Delete <code>/src/components/Styleguide*</code> and <code>/src/components/GraphQL*</code></li>\\n    <li>Delete <code>/sitecore/definitions/components/Styleguide*</code>, <code>/sitecore/definitions/templates/Styleguide*</code>, and <code>/sitecore/definitions/components/GraphQL*</code></li>\\n    <li>Delete <code>/data/component-content/Styleguide</code></li>\\n    <li>Delete <code>/data/content/Styleguide</code></li>\\n    <li>Delete <code>/data/routes/styleguide</code> and <code>/data/routes/graphql</code></li>\\n    <li>Delete <code>/data/dictionary/*.yml</code></li>\\n  </ol>\\n</div>\\n\"\n              }\n            }\n          }\n        ]\n      }\n    }\n  }\n}
\n

Based on this JSON the final page is built.

\n

And now…

\n

This is just the first step… all app specific code must be rewritten in TypeScript. I will keep you posted.

\n

Interesting stuff to investigate in this context:

\n","timeToRead":12,"frontmatter":{"title":"Developing React components in Typescript with Sitecore JSS 9.1","date":"January 29, 2019","spoiler":"At my company we use TypeScript for front-end development where possible. In my first steps in using JSS I wanted to build my first component in TypeScript, which did not work out of the box. In this post I describe how I got ypeScript compilation working."},"fields":{"slug":"/sitecore_jss_typescript/"}}},"pageContext":{"slug":"/sitecore_jss_typescript/","previous":{"fields":{"slug":"/weblog-deployment-to-static-azure-website/"},"frontmatter":{"title":"Weblog deployment to a static Microsoft Azure website"}},"next":{"fields":{"slug":"/sitecore_jss_typescript_node/"},"frontmatter":{"title":"Render Sitecore 9.1 JSS site using separate node server"}}}}