
Introduction
I’ve been working with React, webpack and TypeScript for almost two years, and it still surprise me when I look for a really custom solution and I end up with a generic solution which will solve my specific problem in a generic way.
Today, I am going to write about how to configure webpack to provide different Module path depending on our configuration. In my scenario I have two different APIs one is the “REAL” one and other is the “MOCK”. And I’d like to use the mocked API for development configuration and the real one for Production.
Let’s say we have a function to get all terms of a given taxonomy term set (in SharePoint Online) in order to display them in a React application. We are going to describe how to create the real API, Mock API, how to configure webpack to do the “generic replacement” and how to use the API.
Real API
export default class TaxonomyAPI { | |
/* | |
* Function to get all terms of a given taxonomy Term Set | |
*/ | |
public static getAllTermsByTermSet(termSetGuid: string, termSetName: string, showOnlyAdvailableForTag: boolean) { | |
return new Promise((resolve, reject) => { | |
SP.SOD.executeFunc("sp.js", "SP.ClientContext", () => { | |
SP.SOD.registerSod("sp.taxonomy.js", SP.Utilities.Utility.getLayoutsPageUrl("sp.taxonomy.js")); | |
SP.SOD.executeFunc("sp.taxonomy.js", "SP.Taxonomy.TaxonomySession", () => { | |
const ctx = SP.ClientContext.get_current(); | |
const taxSession = SP.Taxonomy.TaxonomySession.getTaxonomySession(ctx); | |
const termStore = taxSession.getDefaultSiteCollectionTermStore(); | |
const termSet = termStore.getTermSet(new SP.Guid(termSetGuid)); | |
const terms = termSet.getAllTerms(); | |
ctx.load(terms, "Include(IsRoot, TermsCount, Id, Name, PathOfTerm, IsAvailableForTagging)"); | |
ctx.executeQueryAsync( | |
() => { | |
let items: Array<{}> = []; | |
const termEnumerator = terms.getEnumerator(); | |
while (termEnumerator.moveNext()) { | |
const currentTerm: any = termEnumerator.get_current(); | |
const termObj: any = { | |
label: currentTerm.get_name(), | |
value: currentTerm.get_id().toString() | |
}; | |
items = [...items, termObj]; | |
} | |
resolve(items); | |
}, | |
(sender, args) => { | |
reject(args.get_message()); | |
}); | |
}); | |
}); | |
}); | |
} | |
} |
Mock API
// delay to emulate api call | |
const delay: number = 500; | |
export default class TaxonomyAPI { | |
/* | |
* MOCK Function to get all terms of a given taxonomy Term Set | |
*/ | |
public static getAllTermsByTermSet(termSetGuid: string, termSetName: string, showOnlyAdvailableForTag: boolean) { | |
return new Promise((resolve, reject) => { | |
setTimeout(() => { | |
const numberOfItems = 1500; | |
let items: Array<{}> = []; | |
for (let i = 0; i <= numberOfItems; i++) { | |
const tempLabel = "Word " + i; | |
const termObj: any = { | |
label: tempLabel, | |
value: tempLabel, | |
}; | |
items = [...items, termObj]; | |
} | |
resolve(items); | |
}, delay); | |
}); | |
} | |
} |
How to configure webpack
My recommendation is using a NormalModuleReplacementPlugin in our development configuration for webpack:
module.exports = { | |
// target, entry, output, etc.. | |
plugins: [ | |
// replacement for Mock APIs | |
new webpack.NormalModuleReplacementPlugin(/(\.*)\/api\/(\.*)/, function (resource) { | |
resource.request = resource.request.replace(/api/, `/apiMock/`); | |
}) | |
] | |
} |
Usage
// It is important to have the following folder structure | |
// root | |
// | | |
// |-- containers | |
// | | | |
// | |-- MyAppContainer | |
// | | | |
// | |-- MyAppContainer.tsx (here is where we use the api) | |
// | | |
// |-- api | |
// | | | |
// | |-- SP.Taxonomy.ts | |
// | | |
// |-- apiMock | |
// | | | |
// | |-- SP.Taxonomy.ts | |
// we are on MyAppContainer.tsx | |
// we use always the REAL API | |
import TaxonomyAPI from "../../api/SP.Taxonomy"; | |
// this is an example how would like the mock api like (and actually is how it will be converted by webpack dev config) | |
// import TaxonomyAPI from "../../apiMock/SP.Taxonomy"; | |
// we have exactly the same contract or signature for both real and mock | |
TaxonomyAPI.getAllTermsByTermSet("termSetGuid", "termSetName", false) | |
.then(...) | |
.catch(...); |